From 171f755c139764d83e6fc712fcbbcc9d4c5c5956 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:02:34 -0700 Subject: [PATCH 01/52] build: Compile for Sandy Bridge CPU target. (#2651) --- .gitmodules | 4 ---- CMakeLists.txt | 19 ++++++++++--------- externals/CMakeLists.txt | 29 +++++++++++++---------------- externals/cryptopp | 2 +- externals/cryptoppwin | 1 - 5 files changed, 24 insertions(+), 31 deletions(-) delete mode 160000 externals/cryptoppwin diff --git a/.gitmodules b/.gitmodules index 3d0d21c5b..ca229bedd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,10 +6,6 @@ path = externals/cryptopp url = https://github.com/shadps4-emu/ext-cryptopp.git shallow = true -[submodule "externals/cryptoppwin"] - path = externals/cryptoppwin - url = https://github.com/shadps4-emu/ext-cryptoppwin.git - shallow = true [submodule "externals/zlib-ng"] path = externals/zlib-ng url = https://github.com/shadps4-emu/ext-zlib-ng.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c23b7c6..f05587d38 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,10 @@ option(ENABLE_UPDATER "Enables the options to updater" ON) # First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR. if (APPLE AND CMAKE_OSX_ARCHITECTURES) set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}") -else() +elseif (CMAKE_SYSTEM_PROCESSOR) set(BASE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") +else() + set(BASE_ARCHITECTURE "${CMAKE_HOST_SYSTEM_PROCESSOR}") endif() # Next, match common architecture strings down to a known common value. @@ -50,7 +52,12 @@ else() message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}") endif() -if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") +if (ARCHITECTURE STREQUAL "x86_64") + # Set x86_64 target level to Sandy Bridge to generally match what is supported for PS4 guest code with CPU patches. + add_compile_options(-march=sandybridge) +endif() + +if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") # Exclude ARM homebrew path to avoid conflicts when cross compiling. list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew") @@ -995,7 +1002,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers cryptopp::cryptopp) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") @@ -1044,12 +1051,6 @@ if (NOT ENABLE_QT_GUI) target_link_libraries(shadps4 PRIVATE SDL3::SDL3) endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) - target_link_libraries(shadps4 PRIVATE cryptoppwin) -else() - target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp) -endif() - if (ENABLE_QT_GUI) target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia) add_definitions(-DENABLE_QT_GUI) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index bb434677d..3b29a838e 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -26,22 +26,19 @@ if (NOT TARGET fmt::fmt) add_subdirectory(fmt) endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC) - # If it is clang and MSVC we will add a static lib - # CryptoPP - add_subdirectory(cryptoppwin) - target_include_directories(cryptoppwin INTERFACE cryptoppwin/include) -else() - # CryptoPP - if (NOT TARGET cryptopp::cryptopp) - set(CRYPTOPP_INSTALL OFF) - set(CRYPTOPP_BUILD_TESTING OFF) - set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp) - add_subdirectory(cryptopp-cmake) - file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") - # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file - set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") - endif() +# CryptoPP +if (NOT TARGET cryptopp::cryptopp) + set(CRYPTOPP_INSTALL OFF) + set(CRYPTOPP_BUILD_TESTING OFF) + set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp) + # cryptopp instruction set checks do not account for added compile options, + # so disable extensions in the library config to match our chosen target CPU. + set(CRYPTOPP_DISABLE_AESNI ON) + set(CRYPTOPP_DISABLE_AVX2 ON) + add_subdirectory(cryptopp-cmake) + file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") + # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file + set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") endif() if (NOT TARGET FFmpeg::ffmpeg) diff --git a/externals/cryptopp b/externals/cryptopp index 60f81a77e..ee84a3144 160000 --- a/externals/cryptopp +++ b/externals/cryptopp @@ -1 +1 @@ -Subproject commit 60f81a77e0c9a0e7ffc1ca1bc438ddfa2e43b78e +Subproject commit ee84a3144137ac0a1294c0b4a342dca13b66923f diff --git a/externals/cryptoppwin b/externals/cryptoppwin deleted file mode 160000 index bc3441dd2..000000000 --- a/externals/cryptoppwin +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bc3441dd2d6a9728e747dc0180bc8b9065a2923c From b520994960700e565c56fc722db06be688716a7b Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 19 Mar 2025 12:28:55 +0200 Subject: [PATCH 02/52] New Crowdin updates (#2644) * New translations en_us.ts (Albanian) * New translations en_us.ts (Albanian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Spanish) * New translations en_us.ts (Spanish) * New translations en_us.ts (Spanish) * New translations en_us.ts (Swedish) * New translations en_us.ts (Spanish) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Ukrainian) --- src/qt_gui/translations/es_ES.ts | 332 +++++++++++++++---------------- src/qt_gui/translations/nb_NO.ts | 14 +- src/qt_gui/translations/sq_AL.ts | 16 +- src/qt_gui/translations/sv_SE.ts | 16 +- src/qt_gui/translations/uk_UA.ts | 16 +- src/qt_gui/translations/zh_CN.ts | 2 +- 6 files changed, 198 insertions(+), 198 deletions(-) diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 8861185cb..be3c701a9 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -443,15 +443,15 @@ Config Selection - Config Selection + Selección de Configuraciones Common Config - Common Config + Configuración Estándar Use per-game configs - Use per-game configs + Usar configuraciones por juego L1 / LB @@ -535,78 +535,78 @@ Override Lightbar Color - Override Lightbar Color + Reemplazar el Color de la Barra de Luz Override Color - Override Color + Reemplazar Color Unable to Save - Unable to Save + No se Pudo Guardar Cannot bind axis values more than once - Cannot bind axis values more than once + No se pueden vincular valores del eje más de una vez Save - Save + Guardar Apply - Apply + Aplicar Restore Defaults - Restore Defaults + Restaurar Valores Por Defecto Cancel - Cancel + Cancelar EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Editar Asignaciones de Teclado + Ratón y Mando Use Per-Game configs - Use Per-Game configs + Usar Configuraciones por Juego Error - Error + Error Could not open the file for reading - Could not open the file for reading + No se pudo abrir el archivo para la lectura Could not open the file for writing - Could not open the file for writing + No se pudo abrir el archivo para escritura Save Changes - Save Changes + Guardar Cambios Do you want to save changes? - Do you want to save changes? + ¿Quieres guardar los cambios? Help - Help + Ayuda Do you want to reset your custom default config to the original default config? - Do you want to reset your custom default config to the original default config? + ¿Desea restablecer su configuración predeterminada personalizada a la configuración por defecto original? Do you want to reset this config to your custom default config? - Do you want to reset this config to your custom default config? + ¿Quieres restablecer esta configuración a tu configuración por defecto personalizada? Reset to Default @@ -702,7 +702,7 @@ Never Played - Never Played + Nunca Jugado h @@ -788,7 +788,7 @@ Trophy Viewer - Ver trofeos + Expositor de Trofeos Open Folder... @@ -848,7 +848,7 @@ Delete Trophy - Delete Trophy + Eliminar Trofeo Compatibility... @@ -928,7 +928,7 @@ No log file found for this game! - No log file found for this game! + ¡No se encontró un archivo de registro para este juego! Failed to convert icon. @@ -940,7 +940,7 @@ This game has no saved trophies to delete! - This game has no saved trophies to delete! + ¡Este juego no tiene trofeos guardados para eliminar! Save Data @@ -948,7 +948,7 @@ Trophy - Trophy + Trofeo SFO Viewer for @@ -959,23 +959,23 @@ HelpDialog Quickstart - Quickstart + Inicio Rápido FAQ - FAQ + Preguntas Frecuentes (FAQ) Syntax - Syntax + Sintaxis Special Bindings - Special Bindings + Asignación de Teclas Especiales Keybindings - Keybindings + Asignación de Teclas @@ -1001,55 +1001,55 @@ KBMSettings Configure Controls - Configure Controls + Configurar Controles D-Pad - D-Pad + Cruceta Up - Up + Arriba unmapped - unmapped + sin vincular Left - Left + Izquierda Right - Right + Derecha Down - Down + Abajo Left Analog Halfmode - Left Analog Halfmode + Modo Reducido del Stick Izquierdo hold to move left stick at half-speed - hold to move left stick at half-speed + manten para mover el stick izquierdo a la mitad de la velocidad Left Stick - Left Stick + Stick Izquierdo Config Selection - Config Selection + Selección de Configuraciones Common Config - Common Config + Configuración Estándar Use per-game configs - Use per-game configs + Usar configuraciones por juego L1 @@ -1061,11 +1061,11 @@ Text Editor - Text Editor + Editor de Texto Help - Help + Ayuda R1 @@ -1085,127 +1085,127 @@ Mouse to Joystick - Ratón A Joystick + Ratón a Joystick *press F7 ingame to activate - *press F7 ingame to activate + * presiona F7 en el juego para activar R3 - R3 + R3 Options - Options + Opciones Mouse Movement Parameters - Mouse Movement Parameters + Parámetros Movimiento del Ratón note: click Help Button/Special Keybindings for more information - note: click Help Button/Special Keybindings for more information + nota: haga clic en Botón de ayuda/Asignación de Teclas Especiales para más información Face Buttons - Face Buttons + Botones de Acción Triangle - Triangle + Triángulo Square - Square + Cuadrado Circle - Circle + Círculo Cross - Cross + Equis Right Analog Halfmode - Right Analog Halfmode + Modo Reducido del Stick Derecho hold to move right stick at half-speed - hold to move right stick at half-speed + manten para mover el stick derecho a la mitad de la velocidad Right Stick - Right Stick + Stick Derecho Speed Offset (def 0.125): - Speed Offset (def 0.125): + Compensación de Velocidad (def 0.125): Copy from Common Config - Copy from Common Config + Copiar desde la Configuración Estándar Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + Zona Muerta (def 0.50): Speed Multiplier (def 1.0): - Speed Multiplier (def 1.0): + Multiplicador de Velocidad (def 1.0): Common Config Selected - Common Config Selected + Configuración Estándar Seleccionada This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Este botón copia mapeos de la Configuración Estándar al perfil seleccionado actualmente, y no se puede utilizar cuando el perfil seleccionado es la Configuración Estándar. Copy values from Common Config - Copy values from Common Config + Copiar valores de la Configuración Estándar Do you want to overwrite existing mappings with the mappings from the Common Config? - Do you want to overwrite existing mappings with the mappings from the Common Config? + ¿Quiere sobrescribir los mapeos existentes con los mapeos de la Configuración Estándar? Unable to Save - Unable to Save + No se Pudo Guardar Cannot bind any unique input more than once - Cannot bind any unique input more than once + No se puede vincular ninguna entrada única más de una vez Press a key - Press a key + Pulsa una tecla Cannot set mapping - Cannot set mapping + No se pudo asignar el mapeo Mousewheel cannot be mapped to stick outputs - Mousewheel cannot be mapped to stick outputs + La rueda del ratón no puede ser mapeada al stick Save - Save + Guardar Apply - Apply + Aplicar Restore Defaults - Restore Defaults + Restaurar Valores Por Defecto Cancel - Cancel + Cancelar @@ -1284,11 +1284,11 @@ List View - Vista de lista + Vista de Lista Grid View - Vista de cuadrícula + Vista de Cuadrícula Elf Viewer @@ -1296,7 +1296,7 @@ Game Install Directory - Carpeta de instalación de los juegos + Carpeta de Instalación de Juegos Download Cheats/Patches @@ -1304,7 +1304,7 @@ Dump Game List - Volcar lista de juegos + Volcar Lista de Juegos PKG Viewer @@ -1324,11 +1324,11 @@ Game List Icons - Iconos de los juegos + Iconos de Juegos Game List Mode - Tipo de lista + Tipo de Lista Settings @@ -1372,7 +1372,7 @@ Game List - Lista de juegos + Lista de Juegos * Unsupported Vulkan Version @@ -1496,7 +1496,7 @@ PKG is a patch or DLC, please install the game first! - El archivo PKG es un parche o un DLC, ¡debes instalar el juego primero! + El archivo PKG es un parche o DLC, ¡debes instalar el juego primero! Game is already running! @@ -1511,7 +1511,7 @@ PKGViewer Open Folder - Abrir carpeta + Abrir Carpeta PKG ERROR @@ -1523,7 +1523,7 @@ Serial - Numero de serie + Número de Serie Installed @@ -1543,7 +1543,7 @@ App Ver - Versión de aplicación + Versión de la Aplicación FW @@ -1590,7 +1590,7 @@ Console Language - Idioma de la consola + Idioma de la Consola Emulator Language @@ -1602,7 +1602,7 @@ Enable Separate Update Folder - Habilitar carpeta independiente de actualizaciones + Habilitar Carpeta Independiente de Actualizaciones Default tab when opening settings @@ -1614,7 +1614,7 @@ Show Splash - Mostrar splash + Mostrar Pantalla de Bienvenida Enable Discord Rich Presence @@ -1622,11 +1622,11 @@ Username - Nombre de usuario + Nombre de Usuario Trophy Key - Clave de trofeos + Clave de Trofeos Trophy @@ -1634,7 +1634,7 @@ Open the custom trophy images/sounds folder - Open the custom trophy images/sounds folder + Abrir la carpeta de trofeos/sonidos personalizados Logger @@ -1642,15 +1642,15 @@ Log Type - Tipo de registro + Tipo de Registro Log Filter - Filtro de registro + Filtro de Registro Open Log Location - Abrir ubicación del registro + Abrir Ubicación del registro Input @@ -1662,11 +1662,11 @@ Hide Cursor - Ocultar cursor + Ocultar Cursor Hide Cursor Idle Timeout - Tiempo de espera para ocultar cursor inactivo + Tiempo de Espera para Ocultar Cursor Inactivo s @@ -1678,7 +1678,7 @@ Back Button Behavior - Comportamiento del botón de retroceso + Comportamiento del Botón de Retroceso Graphics @@ -1694,7 +1694,7 @@ Graphics Device - Dispositivo gráfico + Dispositivo Gráfico Vblank Divider @@ -1706,7 +1706,7 @@ Enable Shaders Dumping - Habilitar volcado de shaders + Habilitar volcado de Shaders Enable NULL GPU @@ -1722,7 +1722,7 @@ Game Folders - Carpetas de juego + Carpetas de Juegos Add... @@ -1738,27 +1738,27 @@ Enable Debug Dumping - Habilitar volcado de depuración + Habilitar Volcado de Depuración Enable Vulkan Validation Layers - Habilitar capas de validación de Vulkan + Habilitar Capas de Validación de Vulkan Enable Vulkan Synchronization Validation - Habilitar validación de sincronización de Vulkan + Habilitar Validación de Sincronización de Vulkan Enable RenderDoc Debugging - Habilitar depuración de RenderDoc + Habilitar Depuración de RenderDoc Enable Crash Diagnostics - Habilitar diagnóstico de fallos + Habilitar Diagnóstico de Fallos Collect Shaders - Recopilar shaders + Recopilar Shaders Copy GPU Buffers @@ -1766,11 +1766,11 @@ Host Debug Markers - Host Debug Markers + Marcadores de Depuración del Host Guest Debug Markers - Guest Debug Markers + Marcadores de Depuración del Invitado Update @@ -1778,11 +1778,11 @@ Check for Updates at Startup - Buscar actualizaciones al iniciar + Buscar Actualizaciones al Iniciar Always Show Changelog - Mostrar siempre el registro de cambios + Mostrar Siempre el Registro de Cambios Update Channel @@ -1790,7 +1790,7 @@ Check for Updates - Verificar actualizaciones + Buscar Actualizaciones GUI Settings @@ -1802,11 +1802,11 @@ Disable Trophy Notification - Disable Trophy Notification + Desactivar Notificaciones de Trofeos Background Image - Imagen de fondo + Imagen de Fondo Show Background Image @@ -1826,7 +1826,7 @@ Game Compatibility - Game Compatibility + Compatibilidad Display Compatibility Data @@ -1858,7 +1858,7 @@ Point your mouse at an option to display its description. - Coloque el mouse sobre una opción para mostrar su descripción. + Coloque el ratón sobre una opción para mostrar su descripción. Console Language:\nSets the language that the PS4 game uses.\nIt's recommended to set this to a language the game supports, which will vary by region. @@ -1870,7 +1870,7 @@ Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. + Habilitar Carpeta Independiente de Actualizaciónes:\nHabilita el instalar actualizaciones del juego en una carpeta separada para mas facilidad en la gestión.\nPuede crearse manualmente añadiendo la actualización extraída a la carpeta del juego con el nombre "CUSA00000-UPDATE" donde el CUSA ID coincide con el ID del juego. Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. @@ -1886,7 +1886,7 @@ Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + Clave de Trofeos:\nClave utilizada para descifrar trofeos. Debe obtenerse de tu consola desbloqueada.\nSolo debe contener caracteres hexadecimales. Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. @@ -1910,15 +1910,15 @@ Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + Desactivar Notificaciones de trofeos:\nDesactiva las notificaciones de trofeos en el juego. El progreso de trofeos todavía puede ser rastreado usando el Expositor de Trofeos (haz clic derecho en el juego en la ventana principal). Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. - Ocultar Cursor:\nElija cuándo desaparecerá el cursor:\nNunca: Siempre verá el mouse.\nInactivo: Establezca un tiempo para que desaparezca después de estar inactivo.\nSiempre: nunca verá el mouse. + Ocultar Cursor:\nElija cuándo desaparecerá el cursor:\nNunca: Siempre verá el ratón.\nInactivo: Establezca un tiempo para que desaparezca después de estar inactivo.\nSiempre: nunca verá el ratón. Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. - Establezca un tiempo para que el mouse desaparezca después de estar inactivo. + Establezca un tiempo para que el ratón desaparezca después de estar inactivo. Back Button Behavior:\nSets the controller's back button to emulate tapping the specified position on the PS4 touchpad. @@ -1926,15 +1926,15 @@ Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. - Display Compatibility Data:\nDisplays game compatibility information in table view. Enable "Update Compatibility On Startup" to get up-to-date information. + Mostrar Datos de Compatibilidad:\nMuestra información de compatibilidad de juegos en vista de tabla. Habilite "Actualizar Compatibilidad al Iniciar" para obtener información actualizada. Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. - Update Compatibility On Startup:\nAutomatically update the compatibility database when shadPS4 starts. + Actualizar Compatibilidad al Iniciar:\nActualiza automáticamente la base de datos de compatibilidad cuando shadPS4 se inicia. Update Compatibility Database:\nImmediately update the compatibility database. - Update Compatibility Database:\nImmediately update the compatibility database. + Actualizar Base de Datos de Compatibilidad:\nActualizar inmediatamente la base de datos de compatibilidad. Never @@ -1978,7 +1978,7 @@ Enable Shaders Dumping:\nFor the sake of technical debugging, saves the games shaders to a folder as they render. - Habilitar la Volcadura de Sombras:\nPor el bien de la depuración técnica, guarda las sombras del juego en una carpeta mientras se renderizan. + Habilitar la Volcadura de Shaders:\nPor el bien de la depuración técnica, guarda las sombras del juego en una carpeta mientras se renderizan. Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. @@ -1986,7 +1986,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + Habilitar HDR:\nHabilita HDR en juegos que lo soporten.\nTu monitor debe tener soporte para el espacio de color PQ BT2020 y el formato RGB10A2 de cadena de intercambio. Game Folders:\nThe list of folders to check for installed games. @@ -2018,31 +2018,31 @@ Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). - Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10). + Recopilar Shaders:\nNecesitas esto habilitado para editar shaders con el menú de depuración (Ctrl + F10). Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. - Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work. + Diagnóstico de cuelgues:\nCrea un archivo .yaml con información sobre el estado de Vulkan en el momento del cuelgue.\nÚtil para depurar errores de tipo 'Dispositivo perdido' . Con esto activado, deberías habilitar los marcadores de depuración de Host E Invitado.\nNo funciona en GPUs de Intel.\nNecesitas activar las Capas de Validación de Vulkan y el SDK de Vulkan para que funcione. Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. - Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes. + Copiar Búferes de GPU:\nSortea condiciones de carrera que implican envíos de GPU.\nPuede o no ayudar con cuelgues del tipo 0 de PM4. Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marcadores de Depuración del Host:\n Inserta información del emulador como marcadores para comandos AMDGPU específicos, además de proporcionar nombres de depuración.\nCon esto activado, deberías habilitar el diagnóstico de fallos.\nUtil para programas como RenderDoc. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Marcadores de Depuración del Invitado:\n Inserta cualquier marcador que el propio juego ha añadido al búfer de comandos.\nCon esto activado, deberías habilitar el diagnóstico de fallos.\nUtil para programas como RenderDoc. Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + Ruta de Guardado de Datos:\nLa carpeta donde se guardarán los datos del juego. Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + Buscar:\nBusque una carpeta para establecer como ruta de datos guardados. Release @@ -2054,11 +2054,11 @@ Set the volume of the background music. - Set the volume of the background music. + Establece el volumen de la música de fondo. Enable Motion Controls - Habilitar controles de movimiento + Habilitar Controles de Movimiento Save Data Path @@ -2070,15 +2070,15 @@ async - async + asíncrono sync - sync + síncrono Auto Select - Selección automática + Selección Automática Directory to install games @@ -2094,7 +2094,7 @@ Display Mode - Modo de imagen + Modo de Imagen Windowed @@ -2102,15 +2102,15 @@ Fullscreen - Pantalla completa + Pantalla Completa Fullscreen (Borderless) - Pantalla completa (sin bordes) + Pantalla Completa (sin bordes) Window Size - Tamaño de ventana + Tamaño de Ventana W: @@ -2122,90 +2122,90 @@ Separate Log Files - Separate Log Files + Archivos de Registro Independientes Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Archivos de Registro Independientes:\nEscribe un archivo de registro separado para cada juego. Trophy Notification Position - Trophy Notification Position + Posición de Notificación de Trofeos Left - Left + Izquierda Right - Right + Derecha Top - Top + Arriba Bottom - Bottom + Abajo Notification Duration - Notification Duration + Duración de Notificaciones Portable User Folder - Portable User Folder + Carpeta Portable de Usuario Create Portable User Folder from Common User Folder - Create Portable User Folder from Common User Folder + Crear Carpeta Portable de Usuario a partir de la Carpeta de Usuario Estándar Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. - Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Carpeta portable de usuario:\nAlmacena la configuración de shadPS4 y los datos que se aplicarán sólo a la compilación shadPS4 ubicada en la carpeta actual. Reinicia la aplicación después de crear la carpeta portable de usuario para empezar a usarla. Cannot create portable user folder - Cannot create portable user folder + No se pudo crear la carpeta portable de usuario %1 already exists - %1 already exists + %1 ya existe Portable user folder created - Portable user folder created + Carpeta Portable de Usuario Creada %1 successfully created. - %1 successfully created. + %1 creado correctamente. Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Abre la carpeta de trofeos/sonidos personalizados:\nPuedes añadir imágenes y un audio personalizados a los trofeos.\nAñade los archivos a custom_trophy con los siguientes nombres:\ntrophy.wav o trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNota: El sonido sólo funcionará en versiones QT. TrophyViewer Trophy Viewer - Vista de trofeos + Expositor de Trofeos Progress - Progress + Progreso Show Earned Trophies - Show Earned Trophies + Mostrar Trofeos Obtenidos Show Not Earned Trophies - Show Not Earned Trophies + Mostrar Trofeos No Obtenidos Show Hidden Trophies - Show Hidden Trophies + Mostrar Trofeos Ocultos diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index e7c48e426..f627ff640 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -1734,11 +1734,11 @@ Debug - Feilretting + Feilsøking Enable Debug Dumping - Bruk feilrettingsdumping + Bruk feilsøkingsdumping Enable Vulkan Validation Layers @@ -1950,15 +1950,15 @@ Touchpad Left - Berøringsplate Venstre + Berøringsplate venstre Touchpad Right - Berøringsplate Høyre + Berøringsplate høyre Touchpad Center - Berøringsplate Midt + Berøringsplate midten None @@ -2002,7 +2002,7 @@ Enable Debug Dumping:\nSaves the import and export symbols and file header information of the currently running PS4 program to a directory. - Bruk feilrettingsdumping:\nLagrer import- og eksport-symbolene og filoverskrifts-informasjonen til det nåværende kjørende PS4-programmet i en mappe. + Bruk feilsøkingsdumping:\nLagrer import- og eksport-symbolene og filoverskrifts-informasjonen til det nåværende kjørende PS4-programmet i en mappe. Enable Vulkan Validation Layers:\nEnables a system that validates the state of the Vulkan renderer and logs information about its internal state.\nThis will reduce performance and likely change the behavior of emulation. @@ -2058,7 +2058,7 @@ Enable Motion Controls - Bruk bevegelseskontroller + Bruk bevegelsesstyring Save Data Path diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index 3d1f1a222..bf58dc60d 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -559,11 +559,11 @@ Restore Defaults - Restore Defaults + Rikthe Paracaktimet Cancel - Cancel + Anulo @@ -1193,19 +1193,19 @@ Save - Save + Ruaj Apply - Apply + Zbato Restore Defaults - Restore Defaults + Rikthe Paracaktimet Cancel - Cancel + Anulo @@ -1606,7 +1606,7 @@ Default tab when opening settings - Skeda e parazgjedhur kur hapen cilësimet + Skeda e paracaktuar kur hapen cilësimet Show Game Size In List @@ -1850,7 +1850,7 @@ Restore Defaults - Rikthe paracaktimet + Rikthe Paracaktimet Close diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index de3781414..d631859d4 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -551,19 +551,19 @@ Save - Save + Spara Apply - Apply + Tillämpa Restore Defaults - Restore Defaults + Återställ till standard Cancel - Cancel + Avbryt @@ -1193,19 +1193,19 @@ Save - Save + Spara Apply - Apply + Tillämpa Restore Defaults - Restore Defaults + Återställ till standard Cancel - Cancel + Avbryt diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 1c074be6a..6083577fa 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -551,19 +551,19 @@ Save - Save + Зберегти Apply - Apply + Застосувати Restore Defaults - Restore Defaults + За замовчуванням Cancel - Cancel + Відмінити @@ -1193,19 +1193,19 @@ Save - Save + Зберегти Apply - Apply + Застосувати Restore Defaults - Restore Defaults + За замовчуванням Cancel - Cancel + Відмінити diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 80b322112..6e1fcdc95 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1105,7 +1105,7 @@ note: click Help Button/Special Keybindings for more information - 注意:点击帮助按钮 -> Special Bindings 获取更多信息 + 注意:点击帮助按钮 -> 获取更多关于映射特殊键位的信息 Face Buttons From 9298b074fc0b1641d263eecbeeb429ec21566acc Mon Sep 17 00:00:00 2001 From: Missake212 Date: Wed, 19 Mar 2025 20:09:29 +0000 Subject: [PATCH 03/52] Changing "submit report" button behavior in the GUI (#2654) * change code to serial * delete emulator version function * Adding the line back, someone else will need to look into it * fix epic fail --- src/qt_gui/gui_context_menus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 24b421b9d..c49fc99b6 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -521,7 +521,7 @@ public: "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial), QString::fromStdString(m_games[itemID].name))); query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name)); - query.addQueryItem("game-code", QString::fromStdString(m_games[itemID].serial)); + query.addQueryItem("game-serial", QString::fromStdString(m_games[itemID].serial)); query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version)); query.addQueryItem("emulator-version", QString(Common::VERSION)); url.setQuery(query); From 4eb6304076dc77f589ae293e39a8f0400b8565eb Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Wed, 19 Mar 2025 22:18:51 +0100 Subject: [PATCH 04/52] Only display the "Submit a report" button for release builds (#2656) Co-authored-by: Missake212 --- src/qt_gui/gui_context_menus.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index c49fc99b6..d3c51be85 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -116,7 +116,9 @@ public: compatibilityMenu->addAction(updateCompatibility); compatibilityMenu->addAction(viewCompatibilityReport); - compatibilityMenu->addAction(submitCompatibilityReport); + if (Common::isRelease) { + compatibilityMenu->addAction(submitCompatibilityReport); + } menu.addMenu(compatibilityMenu); From 2a05af22e17d3ece3cf04746799c710a60f31334 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Wed, 19 Mar 2025 23:20:00 +0200 Subject: [PATCH 05/52] emit_spirv: Fix comparison type (#2658) --- src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index e2d702389..9f8784797 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp @@ -236,7 +236,7 @@ Id EmitFindILsb64(EmitContext& ctx, Id value) { const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)}; const Id lo_lsb{ctx.OpFindILsb(ctx.U32[1], lo)}; const Id hi_lsb{ctx.OpFindILsb(ctx.U32[1], hi)}; - const Id found_lo{ctx.OpINotEqual(ctx.U32[1], lo_lsb, ctx.ConstU32(u32(-1)))}; + const Id found_lo{ctx.OpINotEqual(ctx.U1[1], lo_lsb, ctx.ConstU32(u32(-1)))}; return ctx.OpSelect(ctx.U32[1], found_lo, lo_lsb, hi_lsb); } From b1885baddaffd6f7c8f26e42e9754b7c71ce8aed Mon Sep 17 00:00:00 2001 From: Dmugetsu <168934208+diegolix29@users.noreply.github.com> Date: Thu, 20 Mar 2025 14:24:47 -0600 Subject: [PATCH 06/52] clock_gettime fixes for windows (#2659) * Using OrbisKernelTimespec under clock_gettime, orbis_clock_gettime, sceKernelClockGettime to fix compatibility issues. * final fix test * Roamic suggestions --- src/core/libraries/kernel/time.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index 42d959885..b7e4c1756 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -172,7 +172,7 @@ static s32 clock_gettime(u32 clock_id, struct timespec* ts) { } #endif -int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct timespec* ts) { +int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* ts) { if (ts == nullptr) { return ORBIS_KERNEL_ERROR_EFAULT; } @@ -265,17 +265,18 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct timespec* ts) { return EINVAL; } - return clock_gettime(pclock_id, ts); + timespec t{}; + int result = clock_gettime(pclock_id, &t); + ts->tv_sec = t.tv_sec; + ts->tv_nsec = t.tv_nsec; + return result; } int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) { - struct timespec ts; - const auto res = orbis_clock_gettime(clock_id, &ts); + const auto res = orbis_clock_gettime(clock_id, tp); if (res < 0) { return ErrnoToSceKernelError(res); } - tp->tv_sec = ts.tv_sec; - tp->tv_nsec = ts.tv_nsec; return ORBIS_OK; } @@ -469,4 +470,4 @@ void RegisterTime(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime); } -} // namespace Libraries::Kernel +} // namespace Libraries::Kernel \ No newline at end of file From c19b692a66b9c1ff8c9036c92fae38aff9ff0ee4 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Fri, 21 Mar 2025 04:26:00 +0800 Subject: [PATCH 07/52] Sync text editor changes to KBM GUI (#2660) Co-authored-by: rainmakerv2 <30595646+jpau02@users.noreply.github.com> --- src/qt_gui/kbm_gui.cpp | 13 +++++-------- src/qt_gui/kbm_gui.h | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp index c148884e9..8777dda95 100644 --- a/src/qt_gui/kbm_gui.cpp +++ b/src/qt_gui/kbm_gui.cpp @@ -15,7 +15,6 @@ #include "ui_kbm_gui.h" HelpDialog* HelpWindow; - KBMSettings::KBMSettings(std::shared_ptr game_info_get, QWidget* parent) : QDialog(parent), m_game_info(game_info_get), ui(new Ui::KBMSettings) { @@ -48,6 +47,7 @@ KBMSettings::KBMSettings(std::shared_ptr game_info_get, QWidget* ui->ProfileComboBox->setCurrentText("Common Config"); ui->TitleLabel->setText("Common Config"); + config_id = "default"; connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) { if (button == ui->buttonBox->button(QDialogButtonBox::Save)) { @@ -72,6 +72,7 @@ KBMSettings::KBMSettings(std::shared_ptr game_info_get, QWidget* connect(ui->TextEditorButton, &QPushButton::clicked, this, [this]() { auto kbmWindow = new EditorDialog(this); kbmWindow->exec(); + SetUIValuestoMappings(config_id); }); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { @@ -84,9 +85,6 @@ KBMSettings::KBMSettings(std::shared_ptr game_info_get, QWidget* connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] { GetGameTitle(); - std::string config_id = (ui->ProfileComboBox->currentText() == "Common Config") - ? "default" - : ui->ProfileComboBox->currentText().toStdString(); SetUIValuestoMappings(config_id); }); @@ -385,10 +383,6 @@ void KBMSettings::SaveKBMConfig(bool CloseOnSave) { lines.push_back(output_string + " = " + input_string); lines.push_back(""); - - std::string config_id = (ui->ProfileComboBox->currentText() == "Common Config") - ? "default" - : ui->ProfileComboBox->currentText().toStdString(); const auto config_file = Config::GetFoolproofKbmConfigFile(config_id); std::fstream file(config_file); int lineCount = 0; @@ -626,6 +620,9 @@ void KBMSettings::GetGameTitle() { } } } + config_id = (ui->ProfileComboBox->currentText() == "Common Config") + ? "default" + : ui->ProfileComboBox->currentText().toStdString(); } void KBMSettings::onHelpClicked() { diff --git a/src/qt_gui/kbm_gui.h b/src/qt_gui/kbm_gui.h index e63d7bd43..06e58eef6 100644 --- a/src/qt_gui/kbm_gui.h +++ b/src/qt_gui/kbm_gui.h @@ -42,7 +42,7 @@ private: QTimer* timer; QPushButton* MappingButton; QList ButtonsList; - + std::string config_id; const std::vector ControllerInputs = { "cross", "circle", "square", "triangle", "l1", "r1", "l2", "r2", "l3", From 2e54afb295035e601e5b7189cda56441f201397d Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 21 Mar 2025 16:12:14 +0200 Subject: [PATCH 08/52] fix debug version for cryptopp (#2664) --- externals/cryptopp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/cryptopp b/externals/cryptopp index ee84a3144..effed0d0b 160000 --- a/externals/cryptopp +++ b/externals/cryptopp @@ -1 +1 @@ -Subproject commit ee84a3144137ac0a1294c0b4a342dca13b66923f +Subproject commit effed0d0b865afc23ed67e0916f83734e4b9b3b7 From 3b2c01272383e1fcd0b82c7873e1ebf1a641aada Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 23 Mar 2025 00:14:04 +0200 Subject: [PATCH 09/52] tagged v0.7.0 --- dist/net.shadps4.shadPS4.metainfo.xml | 3 +++ src/common/version.h | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index c8c9d5c23..99f9e070d 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -37,6 +37,9 @@ Game + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.7.0 + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.6.0 diff --git a/src/common/version.h b/src/common/version.h index e7f6cc817..e27ed505d 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.6.1 WIP"; -constexpr bool isRelease = false; +constexpr char VERSION[] = "0.7.0"; +constexpr bool isRelease = true; } // namespace Common From d7b947dd796bf80422340f6a58756ede8d1be2a3 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 23 Mar 2025 00:27:20 +0200 Subject: [PATCH 10/52] starting v0.7.1 WIP --- src/common/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/version.h b/src/common/version.h index e27ed505d..652f36e6d 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -8,7 +8,7 @@ namespace Common { -constexpr char VERSION[] = "0.7.0"; -constexpr bool isRelease = true; +constexpr char VERSION[] = "0.7.1 WIP"; +constexpr bool isRelease = false; } // namespace Common From 0fa1220eca95514812682140221917af1b8e4932 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sat, 22 Mar 2025 19:27:42 -0300 Subject: [PATCH 11/52] Add option to enable/disable game folders individually (#2662) * Add option to enable/disable game folders individually A checkbox (button to select) game folders has been added to the menu, allowing you to Enable/Disable them without having to remove the folder. config.toml is now saved in alphabetical order * ordering is separation in a function * remove my comment in portuguese :) --- src/common/config.cpp | 137 +++++++++++++++++++++++++++++---- src/common/config.h | 14 +++- src/qt_gui/settings_dialog.cpp | 48 +++++++++--- 3 files changed, 170 insertions(+), 29 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 514024c30..0b5fb8be1 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -81,7 +81,7 @@ static std::string trophyKey; // Gui static bool load_game_size = true; -std::vector settings_install_dirs = {}; +static std::vector settings_install_dirs = {}; std::filesystem::path settings_addon_install_dir = {}; std::filesystem::path save_data_path = {}; u32 main_window_geometry_x = 400; @@ -519,22 +519,34 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_h = h; } -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; +bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) { + for (const auto& install_dir : settings_install_dirs) { + if (install_dir.path == dir) { + return false; + } } - return false; + settings_install_dirs.push_back({dir, enabled}); + return true; } void removeGameInstallDir(const std::filesystem::path& dir) { - auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); + auto iterator = + std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(), + [&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; }); if (iterator != settings_install_dirs.end()) { settings_install_dirs.erase(iterator); } } +void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled) { + auto iterator = + std::find_if(settings_install_dirs.begin(), settings_install_dirs.end(), + [&dir](const GameInstallDir& install_dir) { return install_dir.path == dir; }); + if (iterator != settings_install_dirs.end()) { + iterator->enabled = enabled; + } +} + void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; } @@ -590,8 +602,15 @@ void setEmulatorLanguage(std::string language) { emulator_language = language; } -void setGameInstallDirs(const std::vector& settings_install_dirs_config) { - settings_install_dirs = settings_install_dirs_config; +void setGameInstallDirs(const std::vector& dirs_config) { + settings_install_dirs.clear(); + for (const auto& dir : dirs_config) { + settings_install_dirs.push_back({dir, true}); + } +} + +void setAllGameInstallDirs(const std::vector& dirs_config) { + settings_install_dirs = dirs_config; } void setSaveDataPath(const std::filesystem::path& path) { @@ -614,7 +633,17 @@ u32 getMainWindowGeometryH() { return main_window_geometry_h; } -const std::vector& getGameInstallDirs() { +const std::vector getGameInstallDirs() { + std::vector enabled_dirs; + for (const auto& dir : settings_install_dirs) { + if (dir.enabled) { + enabled_dirs.push_back(dir.path); + } + } + return enabled_dirs; +} + +const std::vector& getAllGameInstallDirs() { return settings_install_dirs; } @@ -809,8 +838,23 @@ void load(const std::filesystem::path& path) { const auto install_dir_array = toml::find_or>(gui, "installDirs", {}); - for (const auto& dir : install_dir_array) { - addGameInstallDir(std::filesystem::path{dir}); + + std::vector install_dirs_enabled; + try { + install_dirs_enabled = toml::find>(gui, "installDirsEnabled"); + } catch (...) { + // If it does not exist, assume that all are enabled. + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + if (install_dirs_enabled.size() < install_dir_array.size()) { + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + settings_install_dirs.clear(); + for (size_t i = 0; i < install_dir_array.size(); i++) { + settings_install_dirs.push_back( + {std::filesystem::path{install_dir_array[i]}, install_dirs_enabled[i]}); } save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {}); @@ -853,6 +897,37 @@ void load(const std::filesystem::path& path) { } } +void sortTomlSections(toml::ordered_value& data) { + toml::ordered_value ordered_data; + std::vector section_order = {"General", "Input", "GPU", "Vulkan", + "Debug", "Keys", "GUI", "Settings"}; + + for (const auto& section : section_order) { + if (data.contains(section)) { + std::vector keys; + for (const auto& item : data.at(section).as_table()) { + keys.push_back(item.first); + } + + std::sort(keys.begin(), keys.end(), [](const std::string& a, const std::string& b) { + return std::lexicographical_compare( + a.begin(), a.end(), b.begin(), b.end(), [](char a_char, char b_char) { + return std::tolower(a_char) < std::tolower(b_char); + }); + }); + + toml::ordered_value ordered_section; + for (const auto& key : keys) { + ordered_section[key] = data.at(section).at(key); + } + + ordered_data[section] = ordered_section; + } + } + + data = ordered_data; +} + void save(const std::filesystem::path& path) { toml::ordered_value data; @@ -922,14 +997,37 @@ void save(const std::filesystem::path& path) { data["Debug"]["CollectShader"] = isShaderDebug; data["Debug"]["isSeparateLogFilesEnabled"] = isSeparateLogFilesEnabled; data["Debug"]["FPSColor"] = isFpsColor; - data["Keys"]["TrophyKey"] = trophyKey; std::vector install_dirs; - for (const auto& dirString : settings_install_dirs) { - install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); + std::vector install_dirs_enabled; + + // temporary structure for ordering + struct DirEntry { + std::string path_str; + bool enabled; + }; + + std::vector sorted_dirs; + for (const auto& dirInfo : settings_install_dirs) { + sorted_dirs.push_back( + {std::string{fmt::UTF(dirInfo.path.u8string()).data}, dirInfo.enabled}); } + + // Sort directories alphabetically + std::sort(sorted_dirs.begin(), sorted_dirs.end(), [](const DirEntry& a, const DirEntry& b) { + return std::lexicographical_compare( + a.path_str.begin(), a.path_str.end(), b.path_str.begin(), b.path_str.end(), + [](char a_char, char b_char) { return std::tolower(a_char) < std::tolower(b_char); }); + }); + + for (const auto& entry : sorted_dirs) { + install_dirs.push_back(entry.path_str); + install_dirs_enabled.push_back(entry.enabled); + } + data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["installDirsEnabled"] = install_dirs_enabled; data["GUI"]["saveDataPath"] = std::string{fmt::UTF(save_data_path.u8string()).data}; data["GUI"]["loadGameSizeEnabled"] = load_game_size; @@ -940,9 +1038,13 @@ void save(const std::filesystem::path& path) { data["GUI"]["showBackgroundImage"] = showBackgroundImage; data["Settings"]["consoleLanguage"] = m_language; + // Sorting of TOML sections + sortTomlSections(data); + std::ofstream file(path, std::ios::binary); file << data; file.close(); + saveMainWindow(path); } @@ -984,6 +1086,9 @@ void saveMainWindow(const std::filesystem::path& path) { data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["recentFiles"] = m_recent_files; + // Sorting of TOML sections + sortTomlSections(data); + std::ofstream file(path, std::ios::binary); file << data; file.close(); diff --git a/src/common/config.h b/src/common/config.h index 82d65d30e..e495be52a 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -9,6 +9,11 @@ namespace Config { +struct GameInstallDir { + std::filesystem::path path; + bool enabled; +}; + enum HideCursorState : s16 { Never, Idle, Always }; void load(const std::filesystem::path& path); @@ -98,7 +103,8 @@ void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); void setChooseHomeTab(const std::string& type); void setSeparateUpdateEnabled(bool use); -void setGameInstallDirs(const std::vector& settings_install_dirs_config); +void setGameInstallDirs(const std::vector& dirs_config); +void setAllGameInstallDirs(const std::vector& dirs_config); void setSaveDataPath(const std::filesystem::path& path); void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); @@ -133,8 +139,9 @@ void setVkGuestMarkersEnabled(bool enable); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -bool addGameInstallDir(const std::filesystem::path& dir); +bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true); void removeGameInstallDir(const std::filesystem::path& dir); +void setGameInstallDirEnabled(const std::filesystem::path& dir, bool enabled); void setAddonInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); @@ -153,7 +160,8 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -const std::vector& getGameInstallDirs(); +const std::vector getGameInstallDirs(); +const std::vector& getAllGameInstallDirs(); std::filesystem::path getAddonInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 41caccec9..a8beef8da 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -246,12 +246,13 @@ SettingsDialog::SettingsDialog(std::span physical_devices, // 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)) { + if (!file_path.empty() && Config::addGameInstallDir(file_path, true)) { QListWidgetItem* item = new QListWidgetItem(file_path_string); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); ui->gameFoldersListWidget->addItem(item); } }); @@ -783,6 +784,17 @@ void SettingsDialog::UpdateSettings() { emit BackgroundOpacityChanged(ui->backgroundImageOpacitySlider->value()); Config::setShowBackgroundImage(ui->showBackgroundImageCheckBox->isChecked()); + std::vector dirs_with_states; + for (int i = 0; i < ui->gameFoldersListWidget->count(); i++) { + QListWidgetItem* item = ui->gameFoldersListWidget->item(i); + QString path_string = item->text(); + auto path = Common::FS::PathFromQString(path_string); + bool enabled = (item->checkState() == Qt::Checked); + + dirs_with_states.push_back({path, enabled}); + } + Config::setAllGameInstallDirs(dirs_with_states); + #ifdef ENABLE_DISCORD_RPC auto* rpc = Common::Singleton::Instance(); if (Config::getEnableDiscordRPC()) { @@ -797,6 +809,7 @@ void SettingsDialog::UpdateSettings() { } void SettingsDialog::ResetInstallFolders() { + ui->gameFoldersListWidget->clear(); std::filesystem::path userdir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); const toml::value data = toml::parse(userdir / "config.toml"); @@ -805,21 +818,36 @@ void SettingsDialog::ResetInstallFolders() { const toml::value& gui = data.at("GUI"); const auto install_dir_array = toml::find_or>(gui, "installDirs", {}); - std::vector settings_install_dirs_config = {}; - for (const auto& dir : install_dir_array) { - if (std::find(settings_install_dirs_config.begin(), settings_install_dirs_config.end(), - dir) == settings_install_dirs_config.end()) { - settings_install_dirs_config.push_back(dir); - } + std::vector install_dirs_enabled; + try { + install_dirs_enabled = toml::find>(gui, "installDirsEnabled"); + } catch (...) { + // If it does not exist, assume that all are enabled. + install_dirs_enabled.resize(install_dir_array.size(), true); } - for (const auto& dir : settings_install_dirs_config) { + if (install_dirs_enabled.size() < install_dir_array.size()) { + install_dirs_enabled.resize(install_dir_array.size(), true); + } + + std::vector settings_install_dirs_config; + + for (size_t i = 0; i < install_dir_array.size(); i++) { + std::filesystem::path dir = install_dir_array[i]; + bool enabled = install_dirs_enabled[i]; + + settings_install_dirs_config.push_back({dir, enabled}); + QString path_string; Common::FS::PathToQString(path_string, dir); + QListWidgetItem* item = new QListWidgetItem(path_string); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); ui->gameFoldersListWidget->addItem(item); } - Config::setGameInstallDirs(settings_install_dirs_config); + + Config::setAllGameInstallDirs(settings_install_dirs_config); } } From a80c4a7f482efd5278daa08c374e78149226942c Mon Sep 17 00:00:00 2001 From: Pavel <68122101+red-prig@users.noreply.github.com> Date: Sun, 23 Mar 2025 01:27:57 +0300 Subject: [PATCH 12/52] Reset previous buffer label instead of current one (#2663) * Reset previous buffer label * Reset flip label also when registering buffer --- src/core/libraries/videoout/driver.cpp | 13 ++++++++++--- src/core/libraries/videoout/driver.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index d2c980882..8f725e549 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -63,6 +63,7 @@ void VideoOutDriver::Close(s32 handle) { main_port.is_open = false; main_port.flip_rate = 0; + main_port.prev_index = -1; ASSERT(main_port.flip_events.empty()); } @@ -133,6 +134,10 @@ int VideoOutDriver::RegisterBuffers(VideoOutPort* port, s32 startIndex, void* co .address_right = 0, }; + // Reset flip label also when registering buffer + port->buffer_labels[startIndex + i] = 0; + port->SignalVoLabel(); + presenter->RegisterVideoOutSurface(group, address); LOG_INFO(Lib_VideoOut, "buffers[{}] = {:#x}", i + startIndex, address); } @@ -190,11 +195,13 @@ void VideoOutDriver::Flip(const Request& req) { } } - // Reset flip label - if (req.index != -1) { - port->buffer_labels[req.index] = 0; + // Reset prev flip label + if (port->prev_index != -1) { + port->buffer_labels[port->prev_index] = 0; port->SignalVoLabel(); } + // save to prev buf index + port->prev_index = req.index; } void VideoOutDriver::DrawBlankFrame() { diff --git a/src/core/libraries/videoout/driver.h b/src/core/libraries/videoout/driver.h index e57b189b5..3b0df43b9 100644 --- a/src/core/libraries/videoout/driver.h +++ b/src/core/libraries/videoout/driver.h @@ -32,6 +32,7 @@ struct VideoOutPort { std::condition_variable vo_cv; std::condition_variable vblank_cv; int flip_rate = 0; + int prev_index = -1; bool is_open = false; bool is_mode_changing = false; // Used to prevent flip during mode change From 1f9ac53c28a12f2ebd33f0b080c342e34a4ed110 Mon Sep 17 00:00:00 2001 From: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Sun, 23 Mar 2025 00:35:42 +0200 Subject: [PATCH 13/52] shader_recompiler: Improve divergence handling and readlane elimintation (#2667) * control_flow_graph: Improve divergence handling * recompiler: Simplify optimization passes Removes a redudant constant propagation and cleans up the passes a little * ir_passes: Add new readlane elimination pass The algorithm has grown complex enough where it deserves its own pass. The old implementation could only handle a single phi level properly, however this one should be able to eliminate vast majority of lane cases remaining. It first performs a traversal of the phi tree to ensure that all phi sources can be rewritten into an expected value and then performs elimintation by recursively duplicating the phi nodes at each step, in order to preserve control flow. * clang format * control_flow_graph: Remove debug code --- CMakeLists.txt | 2 +- .../frontend/control_flow_graph.cpp | 151 ++++++++++-------- .../frontend/control_flow_graph.h | 2 +- .../ir/passes/constant_propagation_pass.cpp | 50 ------ .../ir/passes/hull_shader_transform.cpp | 4 + src/shader_recompiler/ir/passes/ir_passes.h | 4 +- .../ir/passes/readlane_elimination_pass.cpp | 115 +++++++++++++ .../ir/passes/ring_access_elimination.cpp | 5 +- src/shader_recompiler/recompiler.cpp | 17 +- 9 files changed, 211 insertions(+), 139 deletions(-) create mode 100644 src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f05587d38..185205221 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -771,6 +771,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/passes/identity_removal_pass.cpp src/shader_recompiler/ir/passes/ir_passes.h src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp + src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp src/shader_recompiler/ir/passes/resource_tracking_pass.cpp src/shader_recompiler/ir/passes/ring_access_elimination.cpp src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -1121,7 +1122,6 @@ cmrc_add_resource_library(embedded-resources src/images/gold.png src/images/platinum.png src/images/silver.png) - target_link_libraries(shadps4 PRIVATE res::embedded) # ImGui resources diff --git a/src/shader_recompiler/frontend/control_flow_graph.cpp b/src/shader_recompiler/frontend/control_flow_graph.cpp index 126cb4eb6..cf1882b8c 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.cpp +++ b/src/shader_recompiler/frontend/control_flow_graph.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/assert.h" #include "shader_recompiler/frontend/control_flow_graph.h" @@ -39,9 +40,6 @@ static IR::Condition MakeCondition(const GcnInst& inst) { return IR::Condition::Execz; case Opcode::S_CBRANCH_EXECNZ: return IR::Condition::Execnz; - case Opcode::S_AND_SAVEEXEC_B64: - case Opcode::S_ANDN2_B64: - return IR::Condition::Execnz; default: return IR::Condition::True; } @@ -76,9 +74,9 @@ CFG::CFG(Common::ObjectPool& block_pool_, std::span inst_l index_to_pc.resize(inst_list.size() + 1); labels.reserve(LabelReserveSize); EmitLabels(); - EmitDivergenceLabels(); EmitBlocks(); LinkBlocks(); + SplitDivergenceScopes(); } void CFG::EmitLabels() { @@ -112,7 +110,7 @@ void CFG::EmitLabels() { std::ranges::sort(labels); } -void CFG::EmitDivergenceLabels() { +void CFG::SplitDivergenceScopes() { const auto is_open_scope = [](const GcnInst& inst) { // An open scope instruction is an instruction that modifies EXEC // but also saves the previous value to restore later. This indicates @@ -136,64 +134,97 @@ void CFG::EmitDivergenceLabels() { (inst.opcode == Opcode::S_ANDN2_B64 && inst.dst[0].field == OperandField::ExecLo); }; - // Since we will be adding new labels, avoid iterating those as well. - const size_t end_size = labels.size(); - for (u32 l = 0; l < end_size; l++) { - const Label start = labels[l]; - // Stop if we reached end of existing labels. - if (l == end_size - 1) { - break; - } - const Label end = labels[l + 1]; - const size_t end_index = GetIndex(end); - + for (auto blk = blocks.begin(); blk != blocks.end(); blk++) { + auto next_blk = std::next(blk); s32 curr_begin = -1; - s32 last_exec_idx = -1; - for (size_t index = GetIndex(start); index < end_index; index++) { + for (size_t index = blk->begin_index; index <= blk->end_index; index++) { const auto& inst = inst_list[index]; - if (curr_begin != -1) { - // Keep note of the last instruction that does not ignore exec, so we know where - // to end the divergence block without impacting trailing instructions that do. - if (!IgnoresExecMask(inst)) { - last_exec_idx = index; - } - // Consider a close scope on certain instruction types or at the last instruction - // before the next label. - if (is_close_scope(inst) || index == end_index - 1) { - // Only insert a scope if, since the open-scope instruction, there is at least - // one instruction that does not ignore exec. - if (index - curr_begin > 1 && last_exec_idx != -1) { - // Add a label to the instruction right after the open scope call. - // It is the start of a new basic block. - const auto& save_inst = inst_list[curr_begin]; - AddLabel(index_to_pc[curr_begin] + save_inst.length); - // Add a label to the close scope instruction. - // There are 3 cases where we need to close a scope. - // * Close scope instruction inside the block - // * Close scope instruction at the end of the block (cbranch or endpgm) - // * Normal instruction at the end of the block - // If the instruction we want to close the scope at is at the end of the - // block, we do not need to insert a new label. - if (last_exec_idx != end_index - 1) { - // Add the label after the last instruction affected by exec. - const auto& last_exec_inst = inst_list[last_exec_idx]; - AddLabel(index_to_pc[last_exec_idx] + last_exec_inst.length); - } - } - // Reset scope begin. + const bool is_close = is_close_scope(inst); + if ((is_close || index == blk->end_index) && curr_begin != -1) { + // If there are no instructions inside scope don't do anything. + if (index - curr_begin == 1) { curr_begin = -1; + continue; } + // If all instructions in the scope ignore exec masking, we shouldn't insert a + // scope. + const auto start = inst_list.begin() + curr_begin + 1; + if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) { + // Determine the first instruction affected by the exec mask. + do { + ++curr_begin; + } while (IgnoresExecMask(inst_list[curr_begin])); + + // Determine the last instruction affected by the exec mask. + s32 curr_end = index; + while (IgnoresExecMask(inst_list[curr_end])) { + --curr_end; + } + + // Create a new block for the divergence scope. + Block* block = block_pool.Create(); + block->begin = index_to_pc[curr_begin]; + block->end = index_to_pc[curr_end]; + block->begin_index = curr_begin; + block->end_index = curr_end; + block->end_inst = inst_list[curr_end]; + blocks.insert_before(next_blk, *block); + + // If we are inside the parent block, make an epilogue block and jump to it. + if (curr_end != blk->end_index) { + Block* epi_block = block_pool.Create(); + epi_block->begin = index_to_pc[curr_end + 1]; + epi_block->end = blk->end; + epi_block->begin_index = curr_end + 1; + epi_block->end_index = blk->end_index; + epi_block->end_inst = blk->end_inst; + epi_block->cond = blk->cond; + epi_block->end_class = blk->end_class; + epi_block->branch_true = blk->branch_true; + epi_block->branch_false = blk->branch_false; + blocks.insert_before(next_blk, *epi_block); + + // Have divergence block always jump to epilogue block. + block->cond = IR::Condition::True; + block->branch_true = epi_block; + block->branch_false = nullptr; + + // If the parent block fails to enter divergence block make it jump to + // epilogue too + blk->branch_false = epi_block; + } else { + // No epilogue block is needed since the divergence block + // also ends the parent block. Inherit the end condition. + auto& parent_blk = *blk; + ASSERT(blk->cond == IR::Condition::True && blk->branch_true); + block->cond = IR::Condition::True; + block->branch_true = blk->branch_true; + block->branch_false = nullptr; + + // If the parent block didn't enter the divergence scope + // have it jump directly to the next one + blk->branch_false = blk->branch_true; + } + + // Shrink parent block to end right before curr_begin + // and make it jump to divergence block + --curr_begin; + blk->end = index_to_pc[curr_begin]; + blk->end_index = curr_begin; + blk->end_inst = inst_list[curr_begin]; + blk->cond = IR::Condition::Execnz; + blk->end_class = EndClass::Branch; + blk->branch_true = block; + } + // Reset scope begin. + curr_begin = -1; } // Mark a potential start of an exec scope. if (is_open_scope(inst)) { curr_begin = index; - last_exec_idx = -1; } } } - - // Sort labels to make sure block insertion is correct. - std::ranges::sort(labels); } void CFG::EmitBlocks() { @@ -234,22 +265,6 @@ void CFG::LinkBlocks() { for (auto it = blocks.begin(); it != blocks.end(); it++) { auto& block = *it; const auto end_inst{block.end_inst}; - // Handle divergence block inserted here. - if (end_inst.opcode == Opcode::S_AND_SAVEEXEC_B64 || - end_inst.opcode == Opcode::S_ANDN2_B64 || end_inst.IsCmpx()) { - // Blocks are stored ordered by address in the set - auto next_it = std::next(it); - auto* target_block = &(*next_it); - ++target_block->num_predecessors; - block.branch_true = target_block; - - auto merge_it = std::next(next_it); - auto* merge_block = &(*merge_it); - ++merge_block->num_predecessors; - block.branch_false = merge_block; - block.end_class = EndClass::Branch; - continue; - } // If the block doesn't end with a branch we simply // need to link with the next block. diff --git a/src/shader_recompiler/frontend/control_flow_graph.h b/src/shader_recompiler/frontend/control_flow_graph.h index d98d4b05d..0acce3306 100644 --- a/src/shader_recompiler/frontend/control_flow_graph.h +++ b/src/shader_recompiler/frontend/control_flow_graph.h @@ -57,9 +57,9 @@ public: private: void EmitLabels(); - void EmitDivergenceLabels(); void EmitBlocks(); void LinkBlocks(); + void SplitDivergenceScopes(); void AddLabel(Label address) { const auto it = std::ranges::find(labels, address); diff --git a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp index c8a4b13cb..5c66b1115 100644 --- a/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir/passes/constant_propagation_pass.cpp @@ -251,54 +251,6 @@ void FoldCmpClass(IR::Block& block, IR::Inst& inst) { } } -void FoldReadLane(IR::Block& block, IR::Inst& inst) { - const u32 lane = inst.Arg(1).U32(); - IR::Inst* prod = inst.Arg(0).InstRecursive(); - - const auto search_chain = [lane](const IR::Inst* prod) -> IR::Value { - while (prod->GetOpcode() == IR::Opcode::WriteLane) { - if (prod->Arg(2).U32() == lane) { - return prod->Arg(1); - } - prod = prod->Arg(0).InstRecursive(); - } - return {}; - }; - - if (prod->GetOpcode() == IR::Opcode::WriteLane) { - if (const IR::Value value = search_chain(prod); !value.IsEmpty()) { - inst.ReplaceUsesWith(value); - } - return; - } - - if (prod->GetOpcode() == IR::Opcode::Phi) { - boost::container::small_vector phi_args; - for (size_t arg_index = 0; arg_index < prod->NumArgs(); ++arg_index) { - const IR::Inst* arg{prod->Arg(arg_index).InstRecursive()}; - if (arg->GetOpcode() != IR::Opcode::WriteLane) { - return; - } - const IR::Value value = search_chain(arg); - if (value.IsEmpty()) { - continue; - } - phi_args.emplace_back(value); - } - if (std::ranges::all_of(phi_args, [&](IR::Value value) { return value == phi_args[0]; })) { - inst.ReplaceUsesWith(phi_args[0]); - return; - } - const auto insert_point = IR::Block::InstructionList::s_iterator_to(*prod); - IR::Inst* const new_phi{&*block.PrependNewInst(insert_point, IR::Opcode::Phi)}; - new_phi->SetFlags(IR::Type::U32); - for (size_t arg_index = 0; arg_index < phi_args.size(); arg_index++) { - new_phi->AddPhiOperand(prod->PhiBlock(arg_index), phi_args[arg_index]); - } - inst.ReplaceUsesWith(IR::Value{new_phi}); - } -} - void ConstantPropagation(IR::Block& block, IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::IAdd32: @@ -408,8 +360,6 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { case IR::Opcode::SelectF32: case IR::Opcode::SelectF64: return FoldSelect(inst); - case IR::Opcode::ReadLane: - return FoldReadLane(block, inst); case IR::Opcode::FPNeg32: FoldWhenAllImmediates(inst, [](f32 a) { return -a; }); return; diff --git a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp index 48727e32a..5cf8a1525 100644 --- a/src/shader_recompiler/ir/passes/hull_shader_transform.cpp +++ b/src/shader_recompiler/ir/passes/hull_shader_transform.cpp @@ -1,11 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later + #include "common/assert.h" #include "shader_recompiler/info.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/breadth_first_search.h" #include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/opcodes.h" +#include "shader_recompiler/ir/passes/ir_passes.h" #include "shader_recompiler/ir/pattern_matching.h" #include "shader_recompiler/ir/program.h" #include "shader_recompiler/runtime_info.h" @@ -734,6 +736,8 @@ void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info) { } } } + + ConstantPropagationPass(program.post_order_blocks); } } // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/ir_passes.h b/src/shader_recompiler/ir/passes/ir_passes.h index 69628dbfd..760dbb112 100644 --- a/src/shader_recompiler/ir/passes/ir_passes.h +++ b/src/shader_recompiler/ir/passes/ir_passes.h @@ -17,11 +17,11 @@ void IdentityRemovalPass(IR::BlockList& program); void DeadCodeEliminationPass(IR::Program& program); void ConstantPropagationPass(IR::BlockList& program); void FlattenExtendedUserdataPass(IR::Program& program); +void ReadLaneEliminationPass(IR::Program& program); void ResourceTrackingPass(IR::Program& program); void CollectShaderInfoPass(IR::Program& program); void LowerBufferFormatToRaw(IR::Program& program); -void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, - Stage stage); +void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info); void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info); void HullShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); void DomainShaderTransform(IR::Program& program, RuntimeInfo& runtime_info); diff --git a/src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp b/src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp new file mode 100644 index 000000000..fbe382d41 --- /dev/null +++ b/src/shader_recompiler/ir/passes/readlane_elimination_pass.cpp @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shader_recompiler/ir/program.h" + +namespace Shader::Optimization { + +static IR::Inst* SearchChain(IR::Inst* inst, u32 lane) { + while (inst->GetOpcode() == IR::Opcode::WriteLane) { + if (inst->Arg(2).U32() == lane) { + // We found a possible write lane source, return it. + return inst; + } + inst = inst->Arg(0).InstRecursive(); + } + return inst; +} + +static bool IsPossibleToEliminate(IR::Inst* inst, u32 lane) { + // Breadth-first search visiting the right most arguments first + boost::container::small_vector visited; + std::queue queue; + queue.push(inst); + + while (!queue.empty()) { + // Pop one instruction from the queue + IR::Inst* inst{queue.front()}; + queue.pop(); + + // If it's a WriteLane search for possible candidates + if (inst = SearchChain(inst, lane); inst->GetOpcode() == IR::Opcode::WriteLane) { + // We found a possible write lane source, stop looking here. + continue; + } + // If there are other instructions in-between that use the value we can't eliminate. + if (inst->GetOpcode() != IR::Opcode::ReadLane && inst->GetOpcode() != IR::Opcode::Phi) { + return false; + } + // Visit the right most arguments first + for (size_t arg = inst->NumArgs(); arg--;) { + auto arg_value{inst->Arg(arg)}; + if (arg_value.IsImmediate()) { + continue; + } + // Queue instruction if it hasn't been visited + IR::Inst* arg_inst{arg_value.InstRecursive()}; + if (std::ranges::find(visited, arg_inst) == visited.end()) { + visited.push_back(arg_inst); + queue.push(arg_inst); + } + } + } + return true; +} + +using PhiMap = std::unordered_map; + +static IR::Value GetRealValue(PhiMap& phi_map, IR::Inst* inst, u32 lane) { + // If this is a WriteLane op search the chain for a possible candidate. + if (inst = SearchChain(inst, lane); inst->GetOpcode() == IR::Opcode::WriteLane) { + return inst->Arg(1); + } + + // If this is a phi, duplicate it and populate its arguments with real values. + if (inst->GetOpcode() == IR::Opcode::Phi) { + // We are in a phi cycle, use the already duplicated phi. + const auto [it, is_new_phi] = phi_map.try_emplace(inst); + if (!is_new_phi) { + return IR::Value{it->second}; + } + + // Create new phi and insert it right before the old one. + const auto insert_point = IR::Block::InstructionList::s_iterator_to(*inst); + IR::Block* block = inst->GetParent(); + IR::Inst* new_phi{&*block->PrependNewInst(insert_point, IR::Opcode::Phi)}; + new_phi->SetFlags(IR::Type::U32); + it->second = new_phi; + + // Gather all arguments. + for (size_t arg_index = 0; arg_index < inst->NumArgs(); arg_index++) { + IR::Inst* arg_prod = inst->Arg(arg_index).InstRecursive(); + const IR::Value arg = GetRealValue(phi_map, arg_prod, lane); + new_phi->AddPhiOperand(inst->PhiBlock(arg_index), arg); + } + return IR::Value{new_phi}; + } + UNREACHABLE(); +} + +void ReadLaneEliminationPass(IR::Program& program) { + PhiMap phi_map; + for (IR::Block* const block : program.blocks) { + for (IR::Inst& inst : block->Instructions()) { + if (inst.GetOpcode() != IR::Opcode::ReadLane) { + continue; + } + const u32 lane = inst.Arg(1).U32(); + IR::Inst* prod = inst.Arg(0).InstRecursive(); + + // Check simple case of no control flow and phis + if (prod = SearchChain(prod, lane); prod->GetOpcode() == IR::Opcode::WriteLane) { + inst.ReplaceUsesWith(prod->Arg(1)); + continue; + } + + // Traverse the phi tree to see if it's possible to eliminate + if (prod->GetOpcode() == IR::Opcode::Phi && IsPossibleToEliminate(prod, lane)) { + inst.ReplaceUsesWith(GetRealValue(phi_map, prod, lane)); + phi_map.clear(); + } + } + } +} + +} // namespace Shader::Optimization diff --git a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp index d6f1efb12..071b94ac0 100644 --- a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp +++ b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp @@ -11,8 +11,7 @@ namespace Shader::Optimization { -void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, - Stage stage) { +void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info) { auto& info = program.info; const auto& ForEachInstruction = [&](auto func) { @@ -24,7 +23,7 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim } }; - switch (stage) { + switch (program.info.stage) { case Stage::Local: { ForEachInstruction([=](IR::IREmitter& ir, IR::Inst& inst) { const auto opcode = inst.GetOpcode(); diff --git a/src/shader_recompiler/recompiler.cpp b/src/shader_recompiler/recompiler.cpp index 1c132ebbb..5004e0beb 100644 --- a/src/shader_recompiler/recompiler.cpp +++ b/src/shader_recompiler/recompiler.cpp @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/config.h" -#include "common/io_file.h" -#include "common/path_util.h" #include "shader_recompiler/frontend/control_flow_graph.h" #include "shader_recompiler/frontend/decode.h" #include "shader_recompiler/frontend/structured_control_flow.h" @@ -63,26 +60,18 @@ IR::Program TranslateProgram(std::span code, Pools& pools, Info& info program.post_order_blocks = Shader::IR::PostOrder(program.syntax_list.front()); // Run optimization passes - const auto stage = program.info.stage; - Shader::Optimization::SsaRewritePass(program.post_order_blocks); + Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::IdentityRemovalPass(program.blocks); if (info.l_stage == LogicalStage::TessellationControl) { - // Tess passes require previous const prop passes for now (for simplicity). TODO allow - // fine grained folding or opportunistic folding we set an operand to an immediate - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::TessellationPreprocess(program, runtime_info); - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::HullShaderTransform(program, runtime_info); } else if (info.l_stage == LogicalStage::TessellationEval) { - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::TessellationPreprocess(program, runtime_info); - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); Shader::Optimization::DomainShaderTransform(program, runtime_info); } - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); - Shader::Optimization::RingAccessElimination(program, runtime_info, stage); - Shader::Optimization::ConstantPropagationPass(program.post_order_blocks); + Shader::Optimization::RingAccessElimination(program, runtime_info); + Shader::Optimization::ReadLaneEliminationPass(program); Shader::Optimization::FlattenExtendedUserdataPass(program); Shader::Optimization::ResourceTrackingPass(program); Shader::Optimization::LowerBufferFormatToRaw(program); From 6f944ab1173eb478f7d83db3c637badef2289aaf Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Sun, 23 Mar 2025 22:39:15 +0800 Subject: [PATCH 14/52] Fix spacing for translated KBM GUI (#2670) --- src/qt_gui/kbm_gui.ui | 290 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 262 insertions(+), 28 deletions(-) diff --git a/src/qt_gui/kbm_gui.ui b/src/qt_gui/kbm_gui.ui index 0eac88105..c8d63cd00 100644 --- a/src/qt_gui/kbm_gui.ui +++ b/src/qt_gui/kbm_gui.ui @@ -11,10 +11,16 @@ 0 0 - 1193 - 754 + 1234 + 796 + + + 0 + 0 + + Qt::FocusPolicy::StrongFocus @@ -38,8 +44,8 @@ 0 0 - 1173 - 704 + 1214 + 746 @@ -47,8 +53,8 @@ 0 0 - 1171 - 703 + 1211 + 741 @@ -63,7 +69,7 @@ true - + 0 0 @@ -149,6 +155,12 @@ + + + 160 + 0 + + Left @@ -180,6 +192,12 @@ + + + 160 + 0 + + Right @@ -282,11 +300,17 @@ - + 0 0 + + + 344 + 16777215 + + Left Analog Halfmode @@ -319,7 +343,7 @@ - + 0 0 @@ -405,6 +429,12 @@ + + + 160 + 0 + + Left @@ -436,6 +466,12 @@ + + + 160 + 0 + + 179 @@ -528,18 +564,24 @@ - + 0 - + 0 0 + + + 0 + 0 + + 12 @@ -652,6 +694,18 @@ + + + 0 + 0 + + + + + 160 + 0 + + L1 @@ -670,6 +724,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -683,6 +743,12 @@ + + + 0 + 0 + + 160 @@ -707,6 +773,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -724,6 +796,18 @@ + + + 0 + 0 + + + + + 0 + 0 + + true @@ -735,6 +819,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -745,6 +835,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -762,6 +858,12 @@ + + + 0 + 0 + + 160 @@ -786,6 +888,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -799,6 +907,18 @@ + + + 0 + 0 + + + + + 160 + 0 + + R2 @@ -817,6 +937,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -857,7 +983,7 @@ - 420 + 500 200 @@ -887,6 +1013,12 @@ + + + 0 + 0 + + 160 @@ -911,6 +1043,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -924,12 +1062,30 @@ + + + 0 + 0 + + + + + 160 + 0 + + Touchpad Click + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -947,6 +1103,18 @@ + + + 0 + 0 + + + + + 0 + 0 + + Mouse to Joystick @@ -982,6 +1150,12 @@ + + + 0 + 0 + + 160 @@ -1006,6 +1180,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -1019,9 +1199,15 @@ + + + 0 + 0 + + - 145 + 160 0 @@ -1043,6 +1229,12 @@ + + + 0 + 0 + + Qt::FocusPolicy::NoFocus @@ -1064,6 +1256,12 @@ + + + 0 + 0 + + false @@ -1196,6 +1394,25 @@ + + + + + 0 + 0 + + + + + true + false + + + + note: click Help Button/Special Keybindings for more information + + + @@ -1203,19 +1420,6 @@ - - - - - true - false - - - - note: click Help Button/Special Keybindings for more information - - - @@ -1226,7 +1430,7 @@ - + 0 0 @@ -1306,6 +1510,12 @@ + + + 160 + 0 + + Square @@ -1337,6 +1547,12 @@ + + + 160 + 0 + + Circle @@ -1444,6 +1660,12 @@ 0 + + + 344 + 16777215 + + Right Analog Halfmode @@ -1476,7 +1698,7 @@ - + 0 0 @@ -1568,6 +1790,12 @@ + + + 160 + 0 + + Left @@ -1599,6 +1827,12 @@ + + + 160 + 0 + + Right From 99332e4ec281dc6541948cd94c8f7dad83dacee4 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 23 Mar 2025 22:24:49 +0100 Subject: [PATCH 15/52] Handle "-patch" as the suffix for game update folders (#2674) * Handle "-patch" as the suffix for game update folders * clang * clang 2 --- src/core/file_format/pkg.cpp | 2 +- src/core/file_sys/fs.cpp | 4 ++++ src/emulator.cpp | 2 +- src/qt_gui/game_info.cpp | 2 +- src/qt_gui/game_info.h | 6 ++++++ src/qt_gui/gui_context_menus.h | 23 +++++++++++++++++++++-- src/qt_gui/kbm_config_dialog.cpp | 3 ++- src/qt_gui/main_window.cpp | 6 +++--- 8 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp index a6b5eb9a8..ecc5f10a4 100644 --- a/src/core/file_format/pkg.cpp +++ b/src/core/file_format/pkg.cpp @@ -350,7 +350,7 @@ bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem:: auto title_id = GetTitleID(); if (parent_path.filename() != title_id && - !fmt::UTF(extract_path.u8string()).data.ends_with("-UPDATE")) { + !fmt::UTF(extract_path.u8string()).data.ends_with("-patch")) { extractPaths[ndinode_counter] = parent_path / title_id; } else { // DLCs path has different structure diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index ec940503f..4dad44874 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -70,6 +70,10 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea std::filesystem::path host_path = mount->host_path / rel_path; std::filesystem::path patch_path = mount->host_path; patch_path += "-UPDATE"; + if (!std::filesystem::exists(patch_path)) { + patch_path = mount->host_path; + patch_path += "-patch"; + } patch_path /= rel_path; if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && diff --git a/src/emulator.cpp b/src/emulator.cpp index b6586ecfd..4ec62995b 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -80,7 +80,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorshow(); @@ -434,6 +450,9 @@ public: QString folder_path, game_update_path, dlc_path, save_data_path, trophy_data_path; Common::FS::PathToQString(folder_path, m_games[itemID].path); game_update_path = folder_path + "-UPDATE"; + if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) { + game_update_path = folder_path + "-patch"; + } Common::FS::PathToQString( dlc_path, Config::getAddonInstallDir() / Common::FS::PathFromQString(folder_path).parent_path().filename()); diff --git a/src/qt_gui/kbm_config_dialog.cpp b/src/qt_gui/kbm_config_dialog.cpp index 49a6bcd89..1851c591d 100644 --- a/src/qt_gui/kbm_config_dialog.cpp +++ b/src/qt_gui/kbm_config_dialog.cpp @@ -225,7 +225,8 @@ void EditorDialog::loadInstalledGames() { QDir parentFolder(installDir); QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); for (const auto& fileInfo : fileList) { - if (fileInfo.isDir() && !fileInfo.filePath().endsWith("-UPDATE")) { + if (fileInfo.isDir() && (!fileInfo.filePath().endsWith("-UPDATE") || + !fileInfo.filePath().endsWith("-patch"))) { gameComboBox->addItem(fileInfo.fileName()); // Add game name to combo box } } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index bde32a52d..68135048e 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -840,7 +840,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int // Default paths auto game_folder_path = game_install_dir / pkg.GetTitleID(); auto game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-UPDATE") + (std::string{pkg.GetTitleID()} + "-patch") : game_folder_path; const int max_depth = 5; @@ -851,7 +851,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int if (found_game.has_value()) { game_folder_path = found_game.value().parent_path(); game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-UPDATE") + (std::string{pkg.GetTitleID()} + "-patch") : game_folder_path; } } else { @@ -866,7 +866,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int game_folder_path = game_install_dir / pkg.GetTitleID(); } game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-UPDATE") + (std::string{pkg.GetTitleID()} + "-patch") : game_folder_path; } From 10bf3d383c80e3bd5436a9ea99e7d2c96ed4fa96 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sun, 23 Mar 2025 18:25:11 -0300 Subject: [PATCH 16/52] QT: Fix PR 2662 (#2676) --- src/common/config.cpp | 6 +++--- src/common/config.h | 2 +- src/qt_gui/main.cpp | 7 ++++++- src/qt_gui/settings_dialog.cpp | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 0b5fb8be1..fd6538f85 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -82,6 +82,7 @@ static std::string trophyKey; // Gui static bool load_game_size = true; static std::vector settings_install_dirs = {}; +std::vector install_dirs_enabled = {}; std::filesystem::path settings_addon_install_dir = {}; std::filesystem::path save_data_path = {}; u32 main_window_geometry_x = 400; @@ -643,8 +644,8 @@ const std::vector getGameInstallDirs() { return enabled_dirs; } -const std::vector& getAllGameInstallDirs() { - return settings_install_dirs; +const std::vector getGameInstallDirsEnabled() { + return install_dirs_enabled; } std::filesystem::path getAddonInstallDir() { @@ -839,7 +840,6 @@ void load(const std::filesystem::path& path) { const auto install_dir_array = toml::find_or>(gui, "installDirs", {}); - std::vector install_dirs_enabled; try { install_dirs_enabled = toml::find>(gui, "installDirsEnabled"); } catch (...) { diff --git a/src/common/config.h b/src/common/config.h index e495be52a..4202da88a 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -161,7 +161,7 @@ u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); const std::vector getGameInstallDirs(); -const std::vector& getAllGameInstallDirs(); +const std::vector getGameInstallDirsEnabled(); std::filesystem::path getAddonInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 36dc226ae..d70294e40 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -157,8 +157,13 @@ int main(int argc, char* argv[]) { } } + bool allInstallDirsDisabled = + std::all_of(Config::getGameInstallDirsEnabled().begin(), + Config::getGameInstallDirsEnabled().end(), [](bool val) { return !val; }); + // If no game directory is set and no command line argument, prompt for it - if (Config::getGameInstallDirs().empty() && !has_command_line_argument) { + if (Config::getGameInstallDirs().empty() && allInstallDirsDisabled && + !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index a8beef8da..d789f6f48 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -821,7 +821,7 @@ void SettingsDialog::ResetInstallFolders() { std::vector install_dirs_enabled; try { - install_dirs_enabled = toml::find>(gui, "installDirsEnabled"); + install_dirs_enabled = Config::getGameInstallDirsEnabled(); } catch (...) { // If it does not exist, assume that all are enabled. install_dirs_enabled.resize(install_dir_array.size(), true); From 4f8e5dfd7c03b89bc133d96bb19adb28cb62d074 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Mon, 24 Mar 2025 10:25:37 +0200 Subject: [PATCH 17/52] New Crowdin updates (#2671) * New translations en_us.ts (German) * New translations en_us.ts (German) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Turkish) * New translations en_us.ts (Turkish) * New translations en_us.ts (Norwegian Bokmal) --- src/qt_gui/translations/de_DE.ts | 364 +++++++++++++++---------------- src/qt_gui/translations/nb_NO.ts | 2 +- src/qt_gui/translations/pt_BR.ts | 10 +- src/qt_gui/translations/tr_TR.ts | 40 ++-- 4 files changed, 208 insertions(+), 208 deletions(-) diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index d366d1116..7f395c1c8 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -38,7 +38,7 @@ Version: - Version: + Version: Size: @@ -50,7 +50,7 @@ Repository: - Repository: + Quellen: Download Cheats @@ -86,11 +86,11 @@ Cheats - Cheats + Cheats Patches - Patches + Patches Error @@ -407,59 +407,59 @@ ControlSettings Configure Controls - Configure Controls + Steuerung einrichten D-Pad - D-Pad + Steuerkreuz Up - Up + Oben Left - Left + Links Right - Right + Rechts Down - Down + Runter Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Linker Stick tote Zone (def:2 max:127) Left Deadzone - Left Deadzone + Linke Deadzone Left Stick - Left Stick + Linker Analogstick Config Selection - Config Selection + Konfigurationsauswahl Common Config - Common Config + Standard Konfiguration Use per-game configs - Use per-game configs + Benutze Per-Game Einstellungen L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back @@ -467,59 +467,59 @@ R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + Options / Start R3 - R3 + R3 Face Buttons - Face Buttons + Aktionstasten Triangle / Y - Triangle / Y + Dreieck / Y Square / X - Square / X + Quadrat / X Circle / B - Circle / B + Kreis / B Cross / A - Cross / A + Kreuz / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Rechter Stick tote Zone (def:2, max:127) Right Deadzone - Right Deadzone + Rechte tote Zone Right Stick - Right Stick + Rechter Analogstick Color Adjustment - Color Adjustment + Farbanpassung R: @@ -535,46 +535,46 @@ Override Lightbar Color - Override Lightbar Color + Farbe der Leuchtleiste überschreiben Override Color - Override Color + Farbe überschreiben Unable to Save - Unable to Save + Speichern nicht möglich Cannot bind axis values more than once - Cannot bind axis values more than once + Achsenwerte können nicht mehr als einmal gebunden werden Save - Save + Speichern Apply - Apply + Übernehmen Restore Defaults - Restore Defaults + Werkseinstellungen wiederherstellen Cancel - Cancel + Abbrechen EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Tastatur + Maus und Controller Eingabezuordnungen bearbeiten Use Per-Game configs - Use Per-Game configs + Benutze Per-Game Einstellungen Error @@ -582,19 +582,19 @@ Could not open the file for reading - Could not open the file for reading + Datei konnte nicht zum Lesen geöffnet werden Could not open the file for writing - Could not open the file for writing + Datei konnte nicht zum Schreiben geöffnet werden Save Changes - Save Changes + Änderungen Speichern Do you want to save changes? - Do you want to save changes? + Sollen die Änderungen gespeichert werden? Help @@ -602,15 +602,15 @@ Do you want to reset your custom default config to the original default config? - Do you want to reset your custom default config to the original default config? + Möchten Sie Ihre eigene Standardkonfiguration auf die ursprüngliche Standardkonfiguration zurücksetzen? Do you want to reset this config to your custom default config? - Do you want to reset this config to your custom default config? + Möchten Sie diese Konfiguration auf Ihre eigene Standardkonfiguration zurücksetzen? Reset to Default - Reset to Default + Auf Standard zurücksetzen @@ -655,7 +655,7 @@ Directory to install DLC - Directory to install DLC + Verzeichnis zum Installieren von DLC @@ -666,7 +666,7 @@ Name - Name + Name Serial @@ -678,11 +678,11 @@ Region - Region + Region Firmware - Firmware + Firmware Size @@ -690,7 +690,7 @@ Version - Version + Version Path @@ -706,15 +706,15 @@ h - h + h m - m + m s - s + s Compatibility is untested @@ -753,23 +753,23 @@ GameListUtils B - B + B KB - KB + KB MB - MB + MB GB - GB + GB TB - TB + TB @@ -780,7 +780,7 @@ Cheats / Patches - Cheats / Patches + Cheats / Patches SFO Viewer @@ -848,7 +848,7 @@ Delete Trophy - Delete Trophy + Trophäe löschen Compatibility... @@ -904,7 +904,7 @@ DLC - DLC + DLC Delete %1 @@ -924,27 +924,27 @@ This game has no update folder to open! - This game has no update folder to open! + Dieses Spiel hat keinen Update-Ordner zum öffnen! No log file found for this game! - No log file found for this game! + Keine Protokolldatei für dieses Spiel gefunden! Failed to convert icon. - Failed to convert icon. + Fehler beim Konvertieren des Symbols. This game has no save data to delete! - This game has no save data to delete! + Dieses Spiel hat keine Speicherdaten zum Löschen! This game has no saved trophies to delete! - This game has no saved trophies to delete! + Dieses Spiel hat keine gespeicherten Trophäen zum Löschen! Save Data - Save Data + Gespeicherte Daten Trophy @@ -952,7 +952,7 @@ SFO Viewer for - SFO Viewer for + SFO-Betrachter für @@ -963,19 +963,19 @@ FAQ - FAQ + Häufig gestellte Fragen Syntax - Syntax + Syntax Special Bindings - Special Bindings + Spezielle Zuordnungen Keybindings - Keybindings + Tastenbelegung @@ -990,78 +990,78 @@ Install All Queued to Selected Folder - Install All Queued to Selected Folder + Installieren Sie alles aus der Warteschlange in den ausgewählten Ordner Delete PKG File on Install - Delete PKG File on Install + PKG-Datei beim Installieren löschen KBMSettings Configure Controls - Configure Controls + Steuerung konfigurieren D-Pad - D-Pad + Steuerkreuz Up - Up + Oben unmapped - unmapped + nicht zugeordnet Left - Left + Links Right - Right + Rechts Down - Down + Runter Left Analog Halfmode - Left Analog Halfmode + Linker Analog-Halbmodus hold to move left stick at half-speed - hold to move left stick at half-speed + Halten um den linken Analogstick mit Halbgeschwindigkeit zu bewegen Left Stick - Left Stick + Linker Analogstick Config Selection - Config Selection + Konfigurationsauswahl Common Config - Common Config + Allgemeine Konfiguration Use per-game configs - Use per-game configs + Benutze Per-Game Einstellungen L1 - L1 + L1 L2 - L2 + L2 Text Editor - Text Editor + Textbearbeiter Help @@ -1069,143 +1069,143 @@ R1 - R1 + R1 R2 - R2 + R2 L3 - L3 + L3 Touchpad Click - Touchpad Click + Touchpad-Klick Mouse to Joystick - Mouse to Joystick + Maus zu Joystick *press F7 ingame to activate - *press F7 ingame to activate + *Zum Aktivieren F7 ingame drücken R3 - R3 + R3 Options - Options + Options Mouse Movement Parameters - Mouse Movement Parameters + Mausbewegungsparameter note: click Help Button/Special Keybindings for more information - note: click Help Button/Special Keybindings for more information + Hinweis: Klicken Sie auf Hilfe-Button/Special Tastaturbelegungen für weitere Informationen Face Buttons - Face Buttons + Aktionstasten Triangle - Triangle + Dreieck Square - Square + Quadrat Circle - Circle + Kreis Cross - Cross + Kreuz Right Analog Halfmode - Right Analog Halfmode + Rechter Analog-Halbmodus hold to move right stick at half-speed - hold to move right stick at half-speed + Halten um den rechten Analogstick mit Halbgeschwindigkeit zu bewegen Right Stick - Right Stick + Rechter Analogstick Speed Offset (def 0.125): - Speed Offset (def 0.125): + Geschwindigkeitsversatz (Def 0.125): Copy from Common Config - Copy from Common Config + Von allgemeiner Konfiguration kopieren Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + Tote Zone Versatz (Def 0.50): Speed Multiplier (def 1.0): - Speed Multiplier (def 1.0): + Geschwindigkeit Multiplikator (def 1.0): Common Config Selected - Common Config Selected + Allgemeine Konfiguration ausgewählt This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Diese Schaltfläche kopiert Zuordnungen aus der allgemeinen Konfiguration in das aktuell ausgewählte Profil, und kann nicht verwendet werden, wenn das aktuell ausgewählte Profil die allgemeine Konfiguration ist. Copy values from Common Config - Copy values from Common Config + Werte von allgemeiner Konfiguration kopieren Do you want to overwrite existing mappings with the mappings from the Common Config? - Do you want to overwrite existing mappings with the mappings from the Common Config? + Möchten Sie die vorhandenen Zuordnungen mit den Zuordnungen der allgemeinen Konfigurationen überschreiben? Unable to Save - Unable to Save + Speichern nicht möglich Cannot bind any unique input more than once - Cannot bind any unique input more than once + Kann keine eindeutige Eingabe mehr als einmal zuordnen Press a key - Press a key + Drücken Sie eine Taste Cannot set mapping - Cannot set mapping + Kann Zuordnung nicht festlegen Mousewheel cannot be mapped to stick outputs - Mousewheel cannot be mapped to stick outputs + Mausrad kann nicht zu Analogstick Ausgabe zugeordnet werden Save - Save + Speichern Apply - Apply + Übernehmen Restore Defaults - Restore Defaults + Werkseinstellungen wiederherstellen Cancel - Cancel + Abbrechen @@ -1484,27 +1484,27 @@ Run Game - Run Game + Spiel ausführen Eboot.bin file not found - Eboot.bin file not found + Eboot.bin Datei nicht gefunden PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + PKG-Datei (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + PKG ist ein Patch oder DLC, bitte installieren Sie zuerst das Spiel! Game is already running! - Game is already running! + Spiel läuft bereits! shadPS4 - shadPS4 + shadPS4 @@ -1527,7 +1527,7 @@ Installed - Installed + Installiert Size @@ -1543,11 +1543,11 @@ App Ver - App Ver + App Ver FW - FW + FW Region @@ -1555,7 +1555,7 @@ Flags - Flags + Markierungen Path @@ -1571,7 +1571,7 @@ Package - Package + Paket @@ -1598,7 +1598,7 @@ Emulator - Emulator + Emulator Enable Separate Update Folder @@ -1634,11 +1634,11 @@ Open the custom trophy images/sounds folder - Open the custom trophy images/sounds folder + Öffne den benutzerdefinierten Ordner für Trophäenbilder/Sounds Logger - Logger + Protokollführer Log Type @@ -1658,7 +1658,7 @@ Cursor - Cursor + Mauszeiger Hide Cursor @@ -1670,11 +1670,11 @@ s - s + s Controller - Controller + Kontroller Back Button Behavior @@ -1714,7 +1714,7 @@ Enable HDR - Enable HDR + HDR aktivieren Paths @@ -1734,7 +1734,7 @@ Debug - Debug + Debug Enable Debug Dumping @@ -1802,19 +1802,19 @@ Disable Trophy Notification - Disable Trophy Notification + Trophäen-Benachrichtigung deaktivieren Background Image - Background Image + Hintergrundbild Show Background Image - Show Background Image + Hintergrundbild anzeigen Opacity - Opacity + Transparenz Play title music @@ -1902,7 +1902,7 @@ Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + Hintergrundbild:\nSteuere die Deckkraft des Spiel-Hintergrundbilds. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. @@ -1986,7 +1986,7 @@ Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. - Enable HDR:\nEnables HDR in games that support it.\nYour monitor must have support for the BT2020 PQ color space and the RGB10A2 swapchain format. + HDR:\nAktiviert HDR in Spielen, die es unterstützen.\nIhr Monitor muss Unterstützung für den BT2020 PQ Farbraum und das RGB10A2 Swapchain Format haben. Game Folders:\nThe list of folders to check for installed games. @@ -2038,23 +2038,23 @@ Save Data Path:\nThe folder where game save data will be saved. - Save Data Path:\nThe folder where game save data will be saved. + Datenpfad speichern:\nDer Ordner, in dem Spieldaten gespeichert werden. Browse:\nBrowse for a folder to set as the save data path. - Browse:\nBrowse for a folder to set as the save data path. + Durchsuchen:\nDurchsuchen eines Ordners, um den Speicherdatenpfad festzulegen. Release - Release + Veröffentlichung Nightly - Nightly + Nightly Set the volume of the background music. - Set the volume of the background music. + Legen Sie die Lautstärke der Hintergrundmusik fest. Enable Motion Controls @@ -2070,11 +2070,11 @@ async - async + asynchron sync - sync + syncron Auto Select @@ -2086,7 +2086,7 @@ Directory to save data - Directory to save data + Verzeichnis um Daten zu speichern Video @@ -2094,43 +2094,43 @@ Display Mode - Display Mode + Anzeigemodus Windowed - Windowed + Fenster Fullscreen - Fullscreen + Vollbild Fullscreen (Borderless) - Fullscreen (Borderless) + Vollbild (randlos) Window Size - Window Size + Fenstergröße W: - W: + W: H: - H: + H: Separate Log Files - Separate Log Files + Separate Protokolldateien Separate Log Files:\nWrites a separate logfile for each game. - Separate Log Files:\nWrites a separate logfile for each game. + Separate Protokolldateien:\nSchreibt für jedes Spiel eine separate Logdatei. Trophy Notification Position - Trophy Notification Position + Trophäen-Benachrichtigungsposition Left @@ -2142,43 +2142,43 @@ Top - Top + Zuoberst Bottom - Bottom + Zuunterst Notification Duration - Notification Duration + Benachrichtigungsdauer Portable User Folder - Portable User Folder + Portable Benutzerordner Create Portable User Folder from Common User Folder - Create Portable User Folder from Common User Folder + Erstellen eines portablen Benutzerordners aus dem allgemeinen Benutzer Ordner Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. - Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Portablen Benutzerordner:\nspeichert ShadPS4 Einstellungen und Daten, die nur auf den ShadPS4 Build im aktuellen Ordner angewendet werden. Starten Sie die App nach dem Erstellen des tragbaren Benutzerordners neu, um sie zu verwenden. Cannot create portable user folder - Cannot create portable user folder + Kann keinen portablen Benutzerordner erstellen %1 already exists - %1 already exists + %1 existiert bereits Portable user folder created - Portable user folder created + Portablen Benutzerordner erstellt %1 successfully created. - %1 successfully created. + %1 erfolgreich erstellt. Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index f627ff640..b257b548b 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -1602,7 +1602,7 @@ Enable Separate Update Folder - Bruk seperat oppdateringsmappe + Bruk separat oppdateringsmappe Default tab when opening settings diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 794215401..e37a3fe96 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -730,7 +730,7 @@ Game displays an image but does not go past the menu - Jogo exibe imagem mas não passa do menu + O jogo exibe imagem mas não passa do menu Game has game-breaking glitches or unplayable performance @@ -1284,11 +1284,11 @@ List View - Visualizar em Lista + Visualização em Lista Grid View - Visualizar em Grade + Visualização em Grade Elf Viewer @@ -2030,11 +2030,11 @@ Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Marcadores de Depuração de Host:\nInsere informações vindo do emulador como marcadores para comandos AMDGPU específicos em torno de comandos Vulkan, além de fornecer nomes de depuração aos recursos.\nSe isso estiver habilitado, ative os Diagnósticos de Falha.\nÚtil para programas como o RenderDoc. + Marcadores de Depuração do Host:\nInsere informações vindo do emulador como marcadores para comandos AMDGPU específicos em torno de comandos Vulkan, além de fornecer nomes de depuração aos recursos.\nSe isso estiver habilitado, ative os Diagnósticos de Falhas.\nÚtil para programas como o RenderDoc. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Marcadores de Depuração de Convidado:\nInsere quaisquer marcadores de depuração que o próprio jogo adicionou ao buffer de comando.\nSe isso estiver habilitado, ative os Diagnósticos de Falha.\nÚtil para programas como o RenderDoc. + Marcadores de Depuração do Convidado:\nInsere quaisquer marcadores de depuração que o próprio jogo adicionou ao buffer de comando.\nSe isso estiver habilitado, ative os Diagnósticos de Falhas.\nÚtil para programas como o RenderDoc. Save Data Path:\nThe folder where game save data will be saved. diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index fd4669369..394704274 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -26,11 +26,11 @@ Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - Cheats/Patches deneysel niteliktedir.\nDikkatli kullanın.\n\nCheat'leri ayrı ayrı indirerek, depo seçerek ve indirme düğmesine tıklayarak indirin.\nPatches sekmesinde tüm patch'leri bir kerede indirebilir, hangi patch'leri kullanmak istediğinizi seçebilir ve seçiminizi kaydedebilirsiniz.\n\nCheats/Patches'i geliştirmediğimiz için,\nproblemleri cheat yazarına bildirin.\n\nYeni bir cheat mi oluşturduğunuz? Şu adresi ziyaret edin:\n + Hileler/Yamalar deneysel özelliklerdir.\nDikkatli kullanın.\n\nHileleri depo seçerek ve indirme düğmesine tıklayarak ayrı ayrı indirin.\nYamalar sekmesinde tüm yamaları tek seferde indirebilir, hangi yamaları kullanmak istediğinizi seçebilir ve seçiminizi kaydedebilirsiniz.\n\nHileleri ve yamaları biz geliştirmediğimiz için\nsorunlarınızı hile geliştiricisine bildirin.\n\nYeni bir hile oluşturduysanız şu adresi ziyaret edin:\n No Image Available - Görüntü Mevcut Değil + Kaynak Mevcut Değil Serial: @@ -70,7 +70,7 @@ Do you want to delete the selected file?\n%1 - Seçilen dosyayı silmek istiyor musunuz?\n%1 + Seçili dosyayı silmek istiyor musunuz?\n%1 Select Patch File: @@ -122,7 +122,7 @@ Success - Başarı + Başarılı Options saved successfully. @@ -138,11 +138,11 @@ File Exists - Dosya Var + Dosya mevcut File already exists. Do you want to replace it? - Dosya zaten var. Üzerine yazmak ister misiniz? + Dosya zaten mevcut. Var olan dosyayı değiştirmek istiyor musunuz? Failed to save file: @@ -313,7 +313,7 @@ Update - Güncelleme + Güncelle No @@ -551,26 +551,26 @@ Save - Save + Kaydet Apply - Apply + Uygula Restore Defaults - Restore Defaults + Varsayılanlara Sıfırla Cancel - Cancel + İptal EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Klavye + Fare ve Kontrolcü tuş atamalarını düzenle Use Per-Game configs @@ -1141,7 +1141,7 @@ Speed Offset (def 0.125): - Speed Offset (def 0.125): + Hız Sapması (varsayılan 0.125): Copy from Common Config @@ -1149,7 +1149,7 @@ Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + Ölü Bölge Sapması (varsayılan 0.50): Speed Multiplier (def 1.0): @@ -1161,7 +1161,7 @@ This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Bu tuş, Ortak Yapılandırma'daki atamaları seçili profile kopyalar ve seçili profil Ortak Yapılandırma ise kullanılamaz. Copy values from Common Config @@ -1193,19 +1193,19 @@ Save - Save + Kaydet Apply - Apply + Uygula Restore Defaults - Restore Defaults + Varsayılanlara Sıfırla Cancel - Cancel + İptal @@ -2030,7 +2030,7 @@ Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. - Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. + Ana Bilgisayar Hata Ayıklama Göstergeleri:\nVulkan komutlarının etrafına belirli AMDGPU komutları için göstergeler gibi emülatör tarafı bilgileri ekler ve kaynaklara hata ayıklama adları verir.\nBunu etkinleştirdiyseniz, Çökme Tanılamaları'nı etkinleştirmelisiniz.\nRenderDoc gibi programlar için faydalıdır. Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc. From 16a68d78eb71c8b3cfa697ba9989d27f3c4ef961 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Mon, 24 Mar 2025 05:25:51 -0300 Subject: [PATCH 18/52] Trophy Viewer - Select Game (#2678) * Trophy Viewer - Select Game * TR - Button in Utils +icon TR - Button in Utils +icon I also made a small correction to the game folder list, where the checkboxes were being filled in incorrectly. --- REUSE.toml | 1 + src/common/config.cpp | 6 ++- src/images/trophy_icon.png | Bin 0 -> 16958 bytes src/qt_gui/gui_context_menus.h | 28 +++++++++++- src/qt_gui/main_window.cpp | 55 ++++++++++++++++++++++ src/qt_gui/main_window_ui.h | 8 ++++ src/qt_gui/translations/en_US.ts | 12 +++++ src/qt_gui/trophy_viewer.cpp | 76 +++++++++++++++++++++++++++---- src/qt_gui/trophy_viewer.h | 20 +++++++- src/shadps4.qrc | 1 + 10 files changed, 193 insertions(+), 14 deletions(-) create mode 100644 src/images/trophy_icon.png diff --git a/REUSE.toml b/REUSE.toml index d9b307d39..793990bd8 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -30,6 +30,7 @@ path = [ "src/images/dump_icon.png", "src/images/exit_icon.png", "src/images/file_icon.png", + "src/images/trophy_icon.png", "src/images/flag_china.png", "src/images/flag_eu.png", "src/images/flag_jp.png", diff --git a/src/common/config.cpp b/src/common/config.cpp index fd6538f85..e0a348fbe 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -645,7 +645,11 @@ const std::vector getGameInstallDirs() { } const std::vector getGameInstallDirsEnabled() { - return install_dirs_enabled; + std::vector enabled_dirs; + for (const auto& dir : settings_install_dirs) { + enabled_dirs.push_back(dir.enabled); + } + return enabled_dirs; } std::filesystem::path getAddonInstallDir() { diff --git a/src/images/trophy_icon.png b/src/images/trophy_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..559e7dbb2c1e2d4b5c5cb3a066a08209b7054839 GIT binary patch literal 16958 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 Xb9M+i^O$<{P6LDsqXhfsB allTrophyGames; + for (const auto& game : m_games) { + TrophyGameInfo gameInfo; + gameInfo.name = QString::fromStdString(game.name); + Common::FS::PathToQString(gameInfo.trophyPath, game.serial); + Common::FS::PathToQString(gameInfo.gameTrpPath, game.path); + + auto update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-UPDATE"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } else { + update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-patch"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } + } + + allTrophyGames.append(gameInfo); + } + + QString gameName = QString::fromStdString(m_games[itemID].name); + TrophyViewer* trophyViewer = + new TrophyViewer(trophyPath, gameTrpPath, gameName, allTrophyGames); trophyViewer->show(); connect(widget->parent(), &QWidget::destroyed, trophyViewer, [trophyViewer]() { trophyViewer->deleteLater(); }); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 68135048e..27551e997 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -593,6 +593,60 @@ void MainWindow::CreateConnects() { pkgViewer->show(); }); + // Trophy Viewer + connect(ui->trophyViewerAct, &QAction::triggered, this, [this]() { + if (m_game_info->m_games.empty()) { + QMessageBox::information( + this, tr("Trophy Viewer"), + tr("No games found. Please add your games to your library first.")); + return; + } + + const auto& firstGame = m_game_info->m_games[0]; + QString trophyPath, gameTrpPath; + Common::FS::PathToQString(trophyPath, firstGame.serial); + Common::FS::PathToQString(gameTrpPath, firstGame.path); + + auto game_update_path = Common::FS::PathFromQString(gameTrpPath); + game_update_path += "-UPDATE"; + if (std::filesystem::exists(game_update_path)) { + Common::FS::PathToQString(gameTrpPath, game_update_path); + } else { + game_update_path = Common::FS::PathFromQString(gameTrpPath); + game_update_path += "-patch"; + if (std::filesystem::exists(game_update_path)) { + Common::FS::PathToQString(gameTrpPath, game_update_path); + } + } + + QVector allTrophyGames; + for (const auto& game : m_game_info->m_games) { + TrophyGameInfo gameInfo; + gameInfo.name = QString::fromStdString(game.name); + Common::FS::PathToQString(gameInfo.trophyPath, game.serial); + Common::FS::PathToQString(gameInfo.gameTrpPath, game.path); + + auto update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-UPDATE"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } else { + update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath); + update_path += "-patch"; + if (std::filesystem::exists(update_path)) { + Common::FS::PathToQString(gameInfo.gameTrpPath, update_path); + } + } + + allTrophyGames.append(gameInfo); + } + + QString gameName = QString::fromStdString(firstGame.name); + TrophyViewer* trophyViewer = + new TrophyViewer(trophyPath, gameTrpPath, gameName, allTrophyGames); + trophyViewer->show(); + }); + // Themes connect(ui->setThemeDark, &QAction::triggered, &m_window_themes, [this]() { m_window_themes.SetWindowTheme(Theme::Dark, ui->mw_searchbar); @@ -1169,6 +1223,7 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite)); ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite)); + ui->trophyViewerAct->setIcon(RecolorIcon(ui->trophyViewerAct->icon(), isWhite)); ui->configureAct->setIcon(RecolorIcon(ui->configureAct->icon(), isWhite)); ui->addElfFolderAct->setIcon(RecolorIcon(ui->addElfFolderAct->icon(), isWhite)); } diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 3ebfcee9e..246c2afd6 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -27,6 +27,7 @@ public: QAction* downloadCheatsPatchesAct; QAction* dumpGameListAct; QAction* pkgViewerAct; + QAction* trophyViewerAct; #ifdef ENABLE_UPDATER QAction* updaterAct; #endif @@ -139,6 +140,10 @@ public: pkgViewerAct = new QAction(MainWindow); pkgViewerAct->setObjectName("pkgViewer"); pkgViewerAct->setIcon(QIcon(":images/file_icon.png")); + trophyViewerAct = new QAction(MainWindow); + trophyViewerAct->setObjectName("trophyViewer"); + trophyViewerAct->setIcon(QIcon(":images/trophy_icon.png")); + #ifdef ENABLE_UPDATER updaterAct = new QAction(MainWindow); updaterAct->setObjectName("updaterAct"); @@ -321,6 +326,7 @@ public: menuUtils->addAction(downloadCheatsPatchesAct); menuUtils->addAction(dumpGameListAct); menuUtils->addAction(pkgViewerAct); + menuUtils->addAction(trophyViewerAct); #ifdef ENABLE_UPDATER menuHelp->addAction(updaterAct); #endif @@ -379,6 +385,8 @@ public: dumpGameListAct->setText( QCoreApplication::translate("MainWindow", "Dump Game List", nullptr)); pkgViewerAct->setText(QCoreApplication::translate("MainWindow", "PKG Viewer", nullptr)); + trophyViewerAct->setText( + QCoreApplication::translate("MainWindow", "Trophy Viewer", nullptr)); mw_searchbar->setPlaceholderText( QCoreApplication::translate("MainWindow", "Search...", nullptr)); menuFile->setTitle(QCoreApplication::translate("MainWindow", "File", nullptr)); diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index 9c9d56076..20cba0378 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + + Progress diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp index bfa47e3cc..bed487605 100644 --- a/src/qt_gui/trophy_viewer.cpp +++ b/src/qt_gui/trophy_viewer.cpp @@ -104,8 +104,10 @@ void TrophyViewer::updateTableFilters() { } } -TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindow() { - this->setWindowTitle(tr("Trophy Viewer")); +TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath, QString gameName, + const QVector& allTrophyGames) + : QMainWindow(), allTrophyGames_(allTrophyGames), currentGameName_(gameName) { + this->setWindowTitle(tr("Trophy Viewer") + " - " + currentGameName_); this->setAttribute(Qt::WA_DeleteOnClose); tabWidget = new QTabWidget(this); @@ -127,11 +129,40 @@ TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindo << "PID"; PopulateTrophyWidget(trophyPath); - QDockWidget* trophyInfoDock = new QDockWidget("", this); + trophyInfoDock = new QDockWidget("", this); QWidget* dockWidget = new QWidget(trophyInfoDock); QVBoxLayout* dockLayout = new QVBoxLayout(dockWidget); dockLayout->setAlignment(Qt::AlignTop); + // ComboBox for game selection + if (!allTrophyGames_.isEmpty()) { + QLabel* gameSelectionLabel = new QLabel(tr("Select Game:"), dockWidget); + dockLayout->addWidget(gameSelectionLabel); + + gameSelectionComboBox = new QComboBox(dockWidget); + for (const auto& game : allTrophyGames_) { + gameSelectionComboBox->addItem(game.name); + } + + // Select current game in ComboBox + if (!currentGameName_.isEmpty()) { + int index = gameSelectionComboBox->findText(currentGameName_); + if (index >= 0) { + gameSelectionComboBox->setCurrentIndex(index); + } + } + + dockLayout->addWidget(gameSelectionComboBox); + + connect(gameSelectionComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, + &TrophyViewer::onGameSelectionChanged); + + QFrame* line = new QFrame(dockWidget); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + dockLayout->addWidget(line); + } + trophyInfoLabel = new QLabel(tr("Progress") + ": 0% (0/0)", dockWidget); trophyInfoLabel->setStyleSheet( "font-weight: bold; font-size: 16px; color: white; background: #333; padding: 5px;"); @@ -162,7 +193,7 @@ TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindo expandButton->setGeometry(80, 0, 27, 27); expandButton->hide(); - connect(expandButton, &QPushButton::clicked, this, [this, trophyInfoDock] { + connect(expandButton, &QPushButton::clicked, this, [this] { trophyInfoDock->setVisible(true); expandButton->hide(); }); @@ -184,13 +215,13 @@ TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindo updateTrophyInfo(); updateTableFilters(); - connect(trophyInfoDock, &QDockWidget::topLevelChanged, this, [this, trophyInfoDock] { + connect(trophyInfoDock, &QDockWidget::topLevelChanged, this, [this] { if (!trophyInfoDock->isVisible()) { expandButton->show(); } }); - connect(trophyInfoDock, &QDockWidget::visibilityChanged, this, [this, trophyInfoDock] { + connect(trophyInfoDock, &QDockWidget::visibilityChanged, this, [this] { if (!trophyInfoDock->isVisible()) { expandButton->show(); } else { @@ -199,6 +230,29 @@ TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindo }); } +void TrophyViewer::onGameSelectionChanged(int index) { + if (index < 0 || index >= allTrophyGames_.size()) { + return; + } + + while (tabWidget->count() > 0) { + QWidget* widget = tabWidget->widget(0); + tabWidget->removeTab(0); + delete widget; + } + + const TrophyGameInfo& selectedGame = allTrophyGames_[index]; + currentGameName_ = selectedGame.name; + gameTrpPath_ = selectedGame.gameTrpPath; + + this->setWindowTitle(tr("Trophy Viewer") + " - " + currentGameName_); + + PopulateTrophyWidget(selectedGame.trophyPath); + + updateTrophyInfo(); + updateTableFilters(); +} + void TrophyViewer::onDockClosed() { if (!trophyInfoDock->isVisible()) { reopenButton->setVisible(true); @@ -389,13 +443,15 @@ void TrophyViewer::PopulateTrophyWidget(QString title) { tabWidget->addTab(tableWidget, tabName.insert(6, " ").replace(0, 1, tabName.at(0).toUpper())); - this->resize(width + 400, 720); - QSize mainWindowSize = QApplication::activeWindow()->size(); - this->resize(mainWindowSize.width() * 0.8, mainWindowSize.height() * 0.8); + if (!this->isMaximized()) { + this->resize(width + 400, 720); + QSize mainWindowSize = QApplication::activeWindow()->size(); + this->resize(mainWindowSize.width() * 0.8, mainWindowSize.height() * 0.8); + } this->show(); tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed); - tableWidget->setColumnWidth(3, 650); + tableWidget->setColumnWidth(3, 500); } this->setCentralWidget(tabWidget); } diff --git a/src/qt_gui/trophy_viewer.h b/src/qt_gui/trophy_viewer.h index 75fb500e7..c63171774 100644 --- a/src/qt_gui/trophy_viewer.h +++ b/src/qt_gui/trophy_viewer.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -12,26 +13,38 @@ #include #include #include +#include #include #include #include #include +#include #include #include "common/types.h" #include "core/file_format/trp.h" +struct TrophyGameInfo { + QString name; + QString trophyPath; + QString gameTrpPath; +}; + class TrophyViewer : public QMainWindow { Q_OBJECT public: - explicit TrophyViewer(QString trophyPath, QString gameTrpPath); + explicit TrophyViewer( + QString trophyPath, QString gameTrpPath, QString gameName = "", + const QVector& allTrophyGames = QVector()); void updateTrophyInfo(); - void updateTableFilters(); void onDockClosed(); void reopenLeftDock(); +private slots: + void onGameSelectionChanged(int index); + private: void PopulateTrophyWidget(QString title); void SetTableItem(QTableWidget* parent, int row, int column, QString str); @@ -39,14 +52,17 @@ private: QTabWidget* tabWidget = nullptr; QStringList headers; QString gameTrpPath_; + QString currentGameName_; TRP trp; QLabel* trophyInfoLabel; QCheckBox* showEarnedCheck; QCheckBox* showNotEarnedCheck; QCheckBox* showHiddenCheck; + QComboBox* gameSelectionComboBox; QPushButton* expandButton; QDockWidget* trophyInfoDock; QPushButton* reopenButton; + QVector allTrophyGames_; std::string GetTrpType(const QChar trp_) { switch (trp_.toLatin1()) { diff --git a/src/shadps4.qrc b/src/shadps4.qrc index a1ff680ed..340756f5c 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -8,6 +8,7 @@ images/stop_icon.png images/utils_icon.png images/file_icon.png + images/trophy_icon.png images/folder_icon.png images/themes_icon.png images/iconsize_icon.png From 1908d26093fcdf409b74eef83cffbb812d447a3f Mon Sep 17 00:00:00 2001 From: mailwl Date: Mon, 24 Mar 2025 16:51:36 +0300 Subject: [PATCH 19/52] lseek: let the host OS set lseek errors (#2370) * Fix lseek(fd, -1, SEEK_SET) for XNA * be sure, if seek really return error * refactoring * let host os set lseek errors --- src/common/io_file.cpp | 14 -------------- src/core/libraries/kernel/file_system.cpp | 1 + 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 067010a26..3db78a145 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -377,20 +377,6 @@ bool IOFile::Seek(s64 offset, SeekOrigin origin) const { return false; } - if (False(file_access_mode & (FileAccessMode::Write | FileAccessMode::Append))) { - u64 size = GetSize(); - if (origin == SeekOrigin::CurrentPosition && Tell() + offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::SetOrigin && (u64)offset > size) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } else if (origin == SeekOrigin::End && offset > 0) { - LOG_ERROR(Common_Filesystem, "Seeking past the end of the file"); - return false; - } - } - errno = 0; const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 0150c11f5..ef3c2fe70 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -289,6 +289,7 @@ size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) { } s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { + LOG_TRACE(Kernel_Fs, "called: offset {} whence {}", offset, whence); auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(d); if (file == nullptr) { From 5c72030fb8cc5227b877e2c84a633eea76ff25aa Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 25 Mar 2025 23:54:32 +0200 Subject: [PATCH 20/52] HLE discmap (#2686) * HLE discmap * improved parameters naming * fixed typo --- src/core/libraries/disc_map/disc_map.cpp | 22 +++++++++++----------- src/core/libraries/disc_map/disc_map.h | 10 ++++++---- src/core/libraries/libs.cpp | 1 + src/emulator.cpp | 3 +-- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/core/libraries/disc_map/disc_map.cpp b/src/core/libraries/disc_map/disc_map.cpp index bb566a149..e8b40e624 100644 --- a/src/core/libraries/disc_map/disc_map.cpp +++ b/src/core/libraries/disc_map/disc_map.cpp @@ -9,29 +9,29 @@ namespace Libraries::DiscMap { -int PS4_SYSV_ABI sceDiscMapGetPackageSize() { - LOG_WARNING(Lib_DiscMap, "(DUMMY) called"); +int PS4_SYSV_ABI sceDiscMapGetPackageSize(s64 fflags, int* ret1, int* ret2) { return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } -int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD() { - LOG_WARNING(Lib_DiscMap, "(DUMMY) called"); +int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD(char* path, s64 offset, s64 nbytes, int* ret) { return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } -int PS4_SYSV_ABI Func_7C980FFB0AA27E7A() { - LOG_ERROR(Lib_DiscMap, "(STUBBED) called"); +int PS4_SYSV_ABI Func_7C980FFB0AA27E7A(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2) { + *flags = 0; + *ret1 = 0; + *ret2 = 0; return ORBIS_OK; } -int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9() { - LOG_ERROR(Lib_DiscMap, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2) { + return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } int PS4_SYSV_ABI Func_E7EBCE96E92F91F8() { - LOG_ERROR(Lib_DiscMap, "(STUBBED) called"); - return ORBIS_OK; + return ORBIS_DISC_MAP_ERROR_NO_BITMAP_INFO; } void RegisterlibSceDiscMap(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/disc_map/disc_map.h b/src/core/libraries/disc_map/disc_map.h index 08abee632..dc8b875ac 100644 --- a/src/core/libraries/disc_map/disc_map.h +++ b/src/core/libraries/disc_map/disc_map.h @@ -10,10 +10,12 @@ class SymbolsResolver; } namespace Libraries::DiscMap { -int PS4_SYSV_ABI sceDiscMapGetPackageSize(); -int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD(); -int PS4_SYSV_ABI Func_7C980FFB0AA27E7A(); -int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9(); +int PS4_SYSV_ABI sceDiscMapGetPackageSize(s64 fflags, int* ret1, int* ret2); +int PS4_SYSV_ABI sceDiscMapIsRequestOnHDD(char* path, s64 offset, s64 nbytes, int* ret); +int PS4_SYSV_ABI Func_7C980FFB0AA27E7A(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2); +int PS4_SYSV_ABI Func_8A828CAEE7EDD5E9(char* path, s64 offset, s64 nbytes, int* flags, int* ret1, + int* ret2); int PS4_SYSV_ABI Func_E7EBCE96E92F91F8(); void RegisterlibSceDiscMap(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 074cf524e..cd0fe650b 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -115,6 +115,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::NpParty::RegisterlibSceNpParty(sym); Libraries::Zlib::RegisterlibSceZlib(sym); Libraries::Hmd::RegisterlibSceHmd(sym); + Libraries::DiscMap::RegisterlibSceDiscMap(sym); } } // namespace Libraries diff --git a/src/emulator.cpp b/src/emulator.cpp index 4ec62995b..5f94f008a 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -289,13 +289,12 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector ModulesToLoad{ + constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {"libSceUlt.sprx", nullptr}, {"libSceJson.sprx", nullptr}, {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal}, - {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceCesCs.sprx", nullptr}, {"libSceFont.sprx", nullptr}, From a1ec8b0a88f8a14fdecc0de83b8e20cbbc9b2e32 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Tue, 25 Mar 2025 23:01:21 +0100 Subject: [PATCH 21/52] Handle compute packets that are split between the ends of two command buffers (#2476) * Squashed initial implementation * Logging for checking if buffers are memory contiguous * Add check to see if first instruction is valid in the next buffer to avoid false positives * Oof * Replace old code with IndecisiveTurtle's new, better implementation * Add `unlikely` keyword to the split packet handling branches Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com> --------- Co-authored-by: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> --- src/video_core/amdgpu/liverpool.cpp | 55 +++++++++++++++++++---------- src/video_core/amdgpu/liverpool.h | 5 ++- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 246c8c947..bfe99c754 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -726,20 +726,39 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span -Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { +Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vqid) { FIBER_ENTER(acb_task_name[vqid]); - const auto& queue = asc_queues[{vqid}]; + auto& queue = asc_queues[{vqid}]; - auto base_addr = reinterpret_cast(acb.data()); - while (!acb.empty()) { - const auto* header = reinterpret_cast(acb.data()); - const u32 type = header->type; - if (type != 3) { - // No other types of packets were spotted so far - UNREACHABLE_MSG("Invalid PM4 type {}", type); + auto base_addr = reinterpret_cast(acb); + while (acb_dwords > 0) { + auto* header = reinterpret_cast(acb); + u32 next_dw_off = header->type3.NumWords() + 1; + + // If we have a buffered packet, use it. + if (queue.tmp_dwords > 0) [[unlikely]] { + header = reinterpret_cast(queue.tmp_packet.data()); + next_dw_off = header->type3.NumWords() + 1 - queue.tmp_dwords; + std::memcpy(queue.tmp_packet.data() + queue.tmp_dwords, acb, next_dw_off * sizeof(u32)); + queue.tmp_dwords = 0; + } + + // If the packet is split across ring boundary, buffer until next submission + if (next_dw_off > acb_dwords) [[unlikely]] { + std::memcpy(queue.tmp_packet.data(), acb, acb_dwords * sizeof(u32)); + queue.tmp_dwords = acb_dwords; + if constexpr (!is_indirect) { + *queue.read_addr += acb_dwords; + *queue.read_addr %= queue.ring_size_dw; + } + break; + } + + if (header->type != 3) { + // No other types of packets were spotted so far + UNREACHABLE_MSG("Invalid PM4 type {}", header->type.Value()); } - const u32 count = header->type3.NumWords(); const PM4ItOpcode opcode = header->type3.opcode; const auto* it_body = reinterpret_cast(header) + 1; switch (opcode) { @@ -749,8 +768,8 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } case PM4ItOpcode::IndirectBuffer: { const auto* indirect_buffer = reinterpret_cast(header); - auto task = ProcessCompute( - {indirect_buffer->Address(), indirect_buffer->ib_size}, vqid); + auto task = ProcessCompute(indirect_buffer->Address(), + indirect_buffer->ib_size, vqid); RESUME_ASC(task, vqid); while (!task.handle.done()) { @@ -800,7 +819,7 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } case PM4ItOpcode::SetShReg: { const auto* set_data = reinterpret_cast(header); - const auto set_size = (count - 1) * sizeof(u32); + const auto set_size = (header->type3.NumWords() - 1) * sizeof(u32); if (set_data->reg_offset >= 0x200 && set_data->reg_offset <= (0x200 + sizeof(ComputeProgram) / 4)) { @@ -895,14 +914,14 @@ Liverpool::Task Liverpool::ProcessCompute(std::span acb, u32 vqid) { } default: UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", - static_cast(opcode), count); + static_cast(opcode), header->type3.NumWords()); } - const auto packet_size_dw = header->type3.NumWords() + 1; - acb = NextPacket(acb, packet_size_dw); + acb += next_dw_off; + acb_dwords -= next_dw_off; if constexpr (!is_indirect) { - *queue.read_addr += packet_size_dw; + *queue.read_addr += next_dw_off; *queue.read_addr %= queue.ring_size_dw; } } @@ -969,7 +988,7 @@ void Liverpool::SubmitAsc(u32 gnm_vqid, std::span acb) { auto& queue = mapped_queues[gnm_vqid]; const auto vqid = gnm_vqid - 1; - const auto& task = ProcessCompute(acb, vqid); + const auto& task = ProcessCompute(acb.data(), acb.size(), vqid); { std::scoped_lock lock{queue.m_access}; queue.submits.emplace(task.handle); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index c18bcd57b..474c04ec2 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -1496,10 +1496,13 @@ public: } struct AscQueueInfo { + static constexpr size_t Pm4BufferSize = 1024; VAddr map_addr; u32* read_addr; u32 ring_size_dw; u32 pipe_id; + std::array tmp_packet; + u32 tmp_dwords; }; Common::SlotVector asc_queues{}; @@ -1541,7 +1544,7 @@ private: Task ProcessGraphics(std::span dcb, std::span ccb); Task ProcessCeUpdate(std::span ccb); template - Task ProcessCompute(std::span acb, u32 vqid); + Task ProcessCompute(const u32* acb, u32 acb_dwords, u32 vqid); void Process(std::stop_token stoken); From 7d0631cf26f983c035c1df9a071a69a28d619001 Mon Sep 17 00:00:00 2001 From: Ked <58560148+k3dr1@users.noreply.github.com> Date: Wed, 26 Mar 2025 20:35:43 +0800 Subject: [PATCH 22/52] Slightly changed how allInstallDirsDisabled is determined (#2688) --- src/qt_gui/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index d70294e40..34e429368 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -158,8 +158,7 @@ int main(int argc, char* argv[]) { } bool allInstallDirsDisabled = - std::all_of(Config::getGameInstallDirsEnabled().begin(), - Config::getGameInstallDirsEnabled().end(), [](bool val) { return !val; }); + std::ranges::all_of(Config::getGameInstallDirsEnabled(), [](bool val) { return !val; }); // If no game directory is set and no command line argument, prompt for it if (Config::getGameInstallDirs().empty() && allInstallDirsDisabled && From d8204641fafe6d1d23c561434328d9fecdb194be Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:03:35 -0500 Subject: [PATCH 23/52] libkernel: Filesystem code cleanup (#2554) * sceKernelOpen: Clean up flag handling * sceKernelOpen: fix params Based on decompilation, the second parameter of _open should be flags. Additionally swaps the return and parameter types to align with our current standards. * sceKernelOpen: Fix errors Based on POSIX spec, if part of the path is missing, ENOENT is the correct return. Additionally, decompilation suggests that open sets errno too. * Fix exports Fixes function exports to align with what they should be, based on what I've seen from decompilation and our module generator. * Proper errno behavior on open Since sceKernelOpen calls posix_open, errno should be set during this process. Simplest way to handle that is to move the actual open code to posix_open and adjust error cases accordingly. * Reorganize open calls, add error log * Improve close Removes the EPERM return, as it makes no sense, and swaps sceKernelClose with posix_close to properly emulate errno behavior. * Fix log on close * posix_open fixups * Readd hack in posix_close It's either this, or removing LLE DiscMap. Or shadow implements posix sockets. * Missing exports Commented out while I gradually work through them all * Remaining placeholder exports * Swap some stuff around I see nothing that suggests "open" only takes two parameters, so this should be completely safe. It's also more accurate to how these are handled in libkernel, and means I won't need to reorganize anything for readv and writev. * Update file_system.cpp * Implement write and posix_write * Oops * Implement posix_readv and sceKernelReadv Also fixes error behavior on readv, as that function shouldn't be returning any kernel error codes. * Move sceKernelUnlink Will deal with this one later, was just annoyed by how it's location doesn't align with the export order. * Cleanup readv * Implement posix_writev and sceKernelWritev Also fixes error behavior on writev, since it shouldn't ever return kernel errors (since our device files return those) * More cleanup on older functions * Swap around sceKernelLseek and posix_lseek This ensures that these have the correct error behavior, and makes their behavior align with the updated implementations for earlier functions. * Update file_system.cpp * Implement read Also fixes error behavior * Swap sceKernelMkdir and posix_mkdir Fixes errno behavior on kernel calls, also fixed some incorrect error returns. * Fix errno behavior on sceKernelRmdir Also reduces function logging to bring it closer to the level of logging seen in other filesystem functions. * Slight clean up of sceKernelStat Fixes error behavior and changes some of the old data types. * Refactor fstat Fixes errno behavior, implements fstat, and shifts exports around based on file position. Might reorganize function locations later though. * Implement posix_ftruncate Implements posix_ftruncate and fixes errno behavior for sceKernelFtruncate * Add missing error conversions for more device functions * Implement posix_rename, fix sceKernelRename errno behavior * Add posix_preadv and posix_pread Also fixes some incorrect error returns, fixes errno behavior, and removes an unnecessary hack. * Fix compile * Implement posix_getdents, getdirentries, and posix_getdirentries Also fixes errno behavior for the kernel variants of these functions. * Fix errno behavior of sceKernelFsync * Implement posix_pwrite and posix_unlink Also fixes errno behavior in related functions. * Update file_system.cpp * Remove SetPosixErrno Ideally, we've handled all possible error conditions before calling these functions, so treat errors in platform-specific code as IO errors and return POSIX_EIO instead. * Update header exports Not sure where these get used, but might as well keep them consistent with the rest of this. * Check if file exists before calling platform-specific code Bloodborne checks if a file doesn't exist using open, checking if it specifically failed with error code ENOENT. To avoid working with platform-specific errnos, add a proper error return for if the file doesn't exist. Fixes a regression in Bloodborne. * Clang Out of all the changes, this is apparently the only thing Clang-Format doesn't like. I'm honestly surprised. * Improve error checks on posix_unlink Just because a file isn't opened doesn't mean the file doesn't exist. Fixes the error returned if host_path.empty(), and removes the error return for when GetFile fails. * Fix the Bloodborne fix * Limit exports to tested functions * More confirmed working exports * Remaining stuff my games can test * FS exports from firmware tests * Bring back missing exports from main I don't have any bootable things that call these, but since they were working well enough on main, they should be fine to readd. * Add export for posix_pread Spotted in Dreams a while back, might as well add it. * Revert "Remove SetPosixErrno" This reverts commit bdfc0c246ce35cde015f3e48a284052ca4caf45e. * Revert SetPosixErrno changes shadow's using it for posix sockets, so significant modifications would introduce unnecessary merge conflicts. * Update comment * Add EACCES errno to SetPosixErrno Seen in Gravity Rush. Also reorganizes the switch case based on the posix errno value, since ordering by errno makes no sense on some OSes. * More export fixups Missed these during my initial pass through FS stuff because they were in kernel.cpp for some reason. * Symbols from FS tests Tested by messing around with firmware elfs, these atleast don't cause any crashes. * Remove inaccurate error behavior Seek can have offsets past the end of a file. Also add logging for two valid whence values that are unsupported on Windows. I'll need to verify that SEEK_HOLE and SEEK_DATA correspond to 3 and 4 respectively, I've yet to check source to verify. * Fix error log Oops * Clang Clang * Remove close hack Since LLE libSceDiscMap is no longer a concern, this hack shouldn't be needed. Since sockets are still stubbed, and close can be used on sockets, I've added a warning log just in case this still occurs in some titles. * Change SetPosixErrno unreachable to warning I changed it to an unreachable in an earlier commit to make testing easier. At this point, having an unreachable for this seems unnecessary, so change it to a warning instead. * Remove Bloodborne hack Games should be able to unlink files that aren't opened file descriptors. As far as I've tested, this doesn't break Bloodborne. --- src/common/io_file.cpp | 5 +- src/common/io_file.h | 2 + src/core/libraries/kernel/file_system.cpp | 862 ++++++++++++++-------- src/core/libraries/kernel/file_system.h | 8 +- src/core/libraries/kernel/kernel.cpp | 28 +- 5 files changed, 570 insertions(+), 335 deletions(-) diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 3db78a145..3efadc6ea 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -125,12 +125,15 @@ namespace { [[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) { switch (origin) { case SeekOrigin::SetOrigin: - default: return SEEK_SET; case SeekOrigin::CurrentPosition: return SEEK_CUR; case SeekOrigin::End: return SEEK_END; + default: + LOG_ERROR(Common_Filesystem, "Unsupported origin {}, defaulting to SEEK_SET", + static_cast(origin)); + return SEEK_SET; } } diff --git a/src/common/io_file.h b/src/common/io_file.h index 45787a092..fb20a2bc5 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -61,6 +61,8 @@ enum class SeekOrigin : u32 { SetOrigin, // Seeks from the start of the file. CurrentPosition, // Seeks from the current file pointer position. End, // Seeks from the end of the file. + SeekHole, // Seeks from the start of the next hole in the file. + SeekData, // Seeks from the start of the next non-hole region in the file. }; class IOFile final { diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index ef3c2fe70..3321559ed 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -18,6 +18,7 @@ #include "core/file_sys/fs.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/kernel/posix_error.h" #include "core/libraries/libs.h" #include "core/memory.h" #include "kernel.h" @@ -57,7 +58,7 @@ static std::map available_device = { namespace Libraries::Kernel { -int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { +s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode); auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); @@ -99,7 +100,8 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { file->m_host_name = mnt->GetHostPath(file->m_guest_name); if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist h->DeleteHandle(handle); - return ORBIS_KERNEL_ERROR_ENOTDIR; + *__Error() = POSIX_ENOENT; + return -1; } else { if (create) { return handle; // dir already exists @@ -116,61 +118,87 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) { } else { file->m_guest_name = path; file->m_host_name = mnt->GetHostPath(file->m_guest_name); + bool exists = std::filesystem::exists(file->m_host_name); int e = 0; - if (read) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); - } else if (write && (create || truncate)) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); - } else if (write && create && append) { // CUSA04729 (appends app0/shaderlist.txt) - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); - } else if (rdwr) { - if (create) { // Create an empty file first. - Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + + if (create) { + if (excl && exists) { + // Error if file exists + h->DeleteHandle(handle); + *__Error() = POSIX_EEXIST; + return -1; } - // RW, then scekernelWrite is called and savedata is written just fine now. - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); - } else if (write) { - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); - } else { - UNREACHABLE(); - } - if (e != 0) { + // Create file if it doesn't exist + Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + } else if (!exists) { + // File to open doesn't exist, return ENOENT h->DeleteHandle(handle); - return ErrnoToSceKernelError(e); + *__Error() = POSIX_ENOENT; + return -1; + } + + if (read) { + // Read only + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); + } else if (write) { + // Write only + if (append) { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); + } else { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); + } + } else if (rdwr) { + // Read and write + if (append) { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); + } else { + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); + } + } else { + // Invalid flags + *__Error() = POSIX_EINVAL; + return -1; + } + + if (truncate && e == 0) { + // If the file was opened successfully and truncate was enabled, reduce size to 0 + file->f.SetSize(0); + } + + if (e != 0) { + // Open failed in platform-specific code, errno needs to be converted. + h->DeleteHandle(handle); + SetPosixErrno(e); + return -1; } } file->is_opened = true; return handle; } -int PS4_SYSV_ABI posix_open(const char* path, int flags, /* SceKernelMode*/ u16 mode) { - LOG_INFO(Kernel_Fs, "posix open redirect to sceKernelOpen"); - int result = sceKernelOpen(path, flags, mode); - // Posix calls different only for their return values +s32 PS4_SYSV_ABI posix_open(const char* filename, s32 flags, u16 mode) { + return open(filename, flags, mode); +} + +s32 PS4_SYSV_ABI sceKernelOpen(const char* path, s32 flags, /* SceKernelMode*/ u16 mode) { + s32 result = open(path, flags, mode); if (result < 0) { - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI open(const char* filename, const char* mode) { - LOG_INFO(Kernel_Fs, "open redirect to sceKernelOpen"); - int result = sceKernelOpen(filename, ORBIS_KERNEL_O_RDWR, 0); - if (result < 0) { - return -1; - } - return result; -} - -int PS4_SYSV_ABI sceKernelClose(int d) { - if (d < 3) { // d probably hold an error code - return ORBIS_KERNEL_ERROR_EPERM; +s32 PS4_SYSV_ABI close(s32 fd) { + if (fd < 3) { + // This is technically possible, but it's usually caused by some stubbed function instead. + LOG_WARNING(Kernel_Fs, "called on an std handle, fd = {}", fd); } auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } if (file->type == Core::FileSys::FileType::Regular) { file->f.Close(); @@ -178,63 +206,54 @@ int PS4_SYSV_ABI sceKernelClose(int d) { file->is_opened = false; LOG_INFO(Kernel_Fs, "Closing {}", file->m_guest_name); // FIXME: Lock file mutex before deleting it? - h->DeleteHandle(d); + h->DeleteHandle(fd); return ORBIS_OK; } -int PS4_SYSV_ABI posix_close(int d) { - int result = sceKernelClose(d); +s32 PS4_SYSV_ABI posix_close(s32 fd) { + return close(fd); +} + +s32 PS4_SYSV_ABI sceKernelClose(s32 fd) { + s32 result = close(fd); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_close: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes) { +s64 PS4_SYSV_ABI write(s32 fd, const void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->write(buf, nbytes); + s64 result = file->device->write(buf, nbytes); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } return file->f.WriteRaw(buf, nbytes); } -int PS4_SYSV_ABI sceKernelUnlink(const char* path) { - if (path == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; +s64 PS4_SYSV_ABI posix_write(s32 fd, const void* buf, size_t nbytes) { + return write(fd, buf, nbytes); +} + +s64 PS4_SYSV_ABI sceKernelWrite(s32 fd, const void* buf, size_t nbytes) { + s64 result = write(fd, buf, nbytes); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } - - auto* h = Common::Singleton::Instance(); - auto* mnt = Common::Singleton::Instance(); - - bool ro = false; - const auto host_path = mnt->GetHostPath(path, &ro); - if (host_path.empty()) { - return ORBIS_KERNEL_ERROR_EACCES; - } - - if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; - } - - if (std::filesystem::is_directory(host_path)) { - return ORBIS_KERNEL_ERROR_EPERM; - } - - auto* file = h->GetFile(host_path); - if (file != nullptr) { - file->f.Unlink(); - } - - LOG_INFO(Kernel_Fs, "Unlinked {}", path); - return ORBIS_OK; + return result; } size_t ReadFile(Common::FS::IOFile& file, void* buf, size_t nbytes) { @@ -246,59 +265,97 @@ size_t ReadFile(Common::FS::IOFile& file, void* buf, size_t nbytes) { return file.ReadRaw(buf, nbytes); } -size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) { +size_t PS4_SYSV_ABI readv(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - int r = file->device->readv(iov, iovcnt); - if (r < 0) { - ErrSceToPosix(r); + size_t result = file->device->readv(iov, iovcnt); + if (result < 0) { + ErrSceToPosix(result); return -1; } - return r; + return result; } size_t total_read = 0; - for (int i = 0; i < iovcnt; i++) { + for (s32 i = 0; i < iovcnt; i++) { total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len); } return total_read; } -size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) { +size_t PS4_SYSV_ABI posix_readv(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + return readv(fd, iov, iovcnt); +} + +size_t PS4_SYSV_ABI sceKernelReadv(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + size_t result = readv(fd, iov, iovcnt); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +size_t PS4_SYSV_ABI writev(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->writev(iov, iovcn); + size_t result = file->device->writev(iov, iovcnt); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } size_t total_written = 0; - for (int i = 0; i < iovcn; i++) { + for (s32 i = 0; i < iovcnt; i++) { total_written += file->f.WriteRaw(iov[i].iov_base, iov[i].iov_len); } return total_written; } -s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { - LOG_TRACE(Kernel_Fs, "called: offset {} whence {}", offset, whence); +size_t PS4_SYSV_ABI posix_writev(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + return writev(fd, iov, iovcnt); +} + +size_t PS4_SYSV_ABI sceKernelWritev(s32 fd, const SceKernelIovec* iov, s32 iovcnt) { + size_t result = writev(fd, iov, iovcnt); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s64 PS4_SYSV_ABI posix_lseek(s32 fd, s64 offset, s32 whence) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->lseek(offset, whence); + s64 result = file->device->lseek(offset, whence); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } Common::FS::SeekOrigin origin{}; @@ -308,53 +365,82 @@ s64 PS4_SYSV_ABI sceKernelLseek(int d, s64 offset, int whence) { origin = Common::FS::SeekOrigin::CurrentPosition; } else if (whence == 2) { origin = Common::FS::SeekOrigin::End; + } else if (whence == 3) { + origin = Common::FS::SeekOrigin::SeekHole; + } else if (whence == 4) { + origin = Common::FS::SeekOrigin::SeekData; + } else { + // whence parameter is invalid + *__Error() = POSIX_EINVAL; + return -1; } if (!file->f.Seek(offset, origin)) { - LOG_CRITICAL(Kernel_Fs, "sceKernelLseek: failed to seek"); - return ORBIS_KERNEL_ERROR_EINVAL; + if (errno != 0) { + // Seek failed in platform-specific code, errno needs to be converted. + SetPosixErrno(errno); + return -1; + } + // Shouldn't be possible, but just in case. + return -1; } - return file->f.Tell(); -} -s64 PS4_SYSV_ABI posix_lseek(int d, s64 offset, int whence) { - s64 result = sceKernelLseek(d, offset, whence); + s64 result = file->f.Tell(); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_lseek: error = {}", result); - ErrSceToPosix(result); + // Tell failed in platform-specific code, errno needs to be converted. + SetPosixErrno(errno); return -1; } return result; } -s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) { +s64 PS4_SYSV_ABI sceKernelLseek(s32 fd, s64 offset, s32 whence) { + s64 result = posix_lseek(fd, offset, whence); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s64 PS4_SYSV_ABI read(s32 fd, void* buf, size_t nbytes) { auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->read(buf, nbytes); + s64 result = file->device->read(buf, nbytes); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } return ReadFile(file->f, buf, nbytes); } -int PS4_SYSV_ABI posix_read(int d, void* buf, size_t nbytes) { - int result = sceKernelRead(d, buf, nbytes); +s64 PS4_SYSV_ABI posix_read(s32 fd, void* buf, size_t nbytes) { + return read(fd, buf, nbytes); +} + +s64 PS4_SYSV_ABI sceKernelRead(s32 fd, void* buf, size_t nbytes) { + s64 result = read(fd, buf, nbytes); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_read: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { +s32 PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { LOG_INFO(Kernel_Fs, "path = {} mode = {}", path, mode); if (path == nullptr) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_ENOTDIR; + return -1; } auto* mnt = Common::Singleton::Instance(); @@ -362,88 +448,79 @@ int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { const auto dir_name = mnt->GetHostPath(path, &ro); if (std::filesystem::exists(dir_name)) { - return ORBIS_KERNEL_ERROR_EEXIST; + *__Error() = POSIX_EEXIST; + return -1; } if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; + *__Error() = POSIX_EROFS; + return -1; } // CUSA02456: path = /aotl after sceSaveDataMount(mode = 1) std::error_code ec; if (dir_name.empty() || !std::filesystem::create_directory(dir_name, ec)) { - return ORBIS_KERNEL_ERROR_EIO; + *__Error() = POSIX_EIO; + return -1; } if (!std::filesystem::exists(dir_name)) { - return ORBIS_KERNEL_ERROR_ENOENT; + *__Error() = POSIX_ENOENT; + return -1; } return ORBIS_OK; } -int PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { - int result = sceKernelMkdir(path, mode); +s32 PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { + s32 result = posix_mkdir(path, mode); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_mkdir: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelRmdir(const char* path) { +s32 PS4_SYSV_ABI posix_rmdir(const char* path) { auto* mnt = Common::Singleton::Instance(); bool ro = false; const std::filesystem::path dir_name = mnt->GetHostPath(path, &ro); - if (dir_name.empty()) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, permission denied", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_EACCES; + if (dir_name.empty() || !std::filesystem::is_directory(dir_name)) { + *__Error() = POSIX_ENOTDIR; + return -1; } if (ro) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, directory is read only", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_EROFS; - } - - if (!std::filesystem::is_directory(dir_name)) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, path is not a directory", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_ENOTDIR; + *__Error() = POSIX_EROFS; + return -1; } if (!std::filesystem::exists(dir_name)) { - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, no such file or directory", - fmt::UTF(dir_name.u8string())); - return ORBIS_KERNEL_ERROR_ENOENT; + *__Error() = POSIX_ENOENT; + return -1; } std::error_code ec; - int result = std::filesystem::remove_all(dir_name, ec); + s32 result = std::filesystem::remove_all(dir_name, ec); - if (!ec) { - LOG_INFO(Kernel_Fs, "Removed directory: {}", fmt::UTF(dir_name.u8string())); - return ORBIS_OK; + if (ec) { + *__Error() = POSIX_EIO; + return -1; } - LOG_ERROR(Kernel_Fs, "Failed to remove directory: {}, error_code={}", - fmt::UTF(dir_name.u8string()), ec.message()); - return ErrnoToSceKernelError(ec.value()); + return ORBIS_OK; } -int PS4_SYSV_ABI posix_rmdir(const char* path) { - int result = sceKernelRmdir(path); +s32 PS4_SYSV_ABI sceKernelRmdir(const char* path) { + s32 result = posix_rmdir(path); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_rmdir: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { +s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path); auto* mnt = Common::Singleton::Instance(); bool ro = false; @@ -452,7 +529,8 @@ int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { const bool is_dir = std::filesystem::is_directory(path_name); const bool is_file = std::filesystem::is_regular_file(path_name); if (!is_dir && !is_file) { - return ORBIS_KERNEL_ERROR_ENOENT; + *__Error() = POSIX_ENOENT; + return -1; } if (std::filesystem::is_directory(path_name)) { sb->st_mode = 0000777u | 0040000u; @@ -462,7 +540,7 @@ int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { // TODO incomplete } else { sb->st_mode = 0000777u | 0100000u; - sb->st_size = static_cast(std::filesystem::file_size(path_name)); + sb->st_size = static_cast(std::filesystem::file_size(path_name)); sb->st_blksize = 512; sb->st_blocks = (sb->st_size + 511) / 512; // TODO incomplete @@ -474,17 +552,16 @@ int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { return ORBIS_OK; } -int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { - int result = sceKernelStat(path, sb); +s32 PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { + s32 result = posix_stat(path, sb); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_stat: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { +s32 PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { auto* mnt = Common::Singleton::Instance(); std::string_view guest_path{path}; for (const auto& prefix : available_device | std::views::keys) { @@ -499,23 +576,165 @@ int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { return ORBIS_OK; } -s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 offset) { - if (d < 3) { - return ORBIS_KERNEL_ERROR_EPERM; +s32 PS4_SYSV_ABI fstat(s32 fd, OrbisKernelStat* sb) { + LOG_INFO(Kernel_Fs, "(PARTIAL) fd = {}", fd); + if (sb == nullptr) { + *__Error() = POSIX_EFAULT; + return -1; } + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + if (file == nullptr) { + *__Error() = POSIX_EBADF; + return -1; + } + std::memset(sb, 0, sizeof(OrbisKernelStat)); + + switch (file->type) { + case Core::FileSys::FileType::Device: { + s32 result = file->device->fstat(sb); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; + } + case Core::FileSys::FileType::Regular: { + sb->st_mode = 0000777u | 0100000u; + sb->st_size = file->f.GetSize(); + sb->st_blksize = 512; + sb->st_blocks = (sb->st_size + 511) / 512; + // TODO incomplete + break; + } + case Core::FileSys::FileType::Directory: { + sb->st_mode = 0000777u | 0040000u; + sb->st_size = 0; + sb->st_blksize = 512; + sb->st_blocks = 0; + // TODO incomplete + break; + } + default: + UNREACHABLE(); + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI posix_fstat(s32 fd, OrbisKernelStat* sb) { + return fstat(fd, sb); +} + +s32 PS4_SYSV_ABI sceKernelFstat(s32 fd, OrbisKernelStat* sb) { + s32 result = fstat(fd, sb); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI posix_ftruncate(s32 fd, s64 length) { + auto* h = Common::Singleton::Instance(); + auto* file = h->GetFile(fd); + + if (file == nullptr) { + *__Error() = POSIX_EBADF; + return -1; + } + + if (file->type == Core::FileSys::FileType::Device) { + s32 result = file->device->ftruncate(length); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; + } + + if (file->m_host_name.empty()) { + *__Error() = POSIX_EACCES; + return -1; + } + + file->f.SetSize(length); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelFtruncate(s32 fd, s64 length) { + s32 result = posix_ftruncate(fd, length); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) { + auto* mnt = Common::Singleton::Instance(); + bool ro = false; + const auto src_path = mnt->GetHostPath(from, &ro); + if (!std::filesystem::exists(src_path)) { + *__Error() = POSIX_ENOENT; + return -1; + } + if (ro) { + *__Error() = POSIX_EROFS; + return -1; + } + const auto dst_path = mnt->GetHostPath(to, &ro); + if (ro) { + *__Error() = POSIX_EROFS; + return -1; + } + const bool src_is_dir = std::filesystem::is_directory(src_path); + const bool dst_is_dir = std::filesystem::is_directory(dst_path); + if (src_is_dir && !dst_is_dir) { + *__Error() = POSIX_ENOTDIR; + return -1; + } + if (!src_is_dir && dst_is_dir) { + *__Error() = POSIX_EISDIR; + return -1; + } + if (dst_is_dir && !std::filesystem::is_empty(dst_path)) { + *__Error() = POSIX_ENOTEMPTY; + return -1; + } + std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { + s32 result = posix_rename(from, to); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s64 PS4_SYSV_ABI posix_preadv(s32 fd, SceKernelIovec* iov, s32 iovcnt, s64 offset) { if (offset < 0) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EINVAL; + return -1; } auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->preadv(iov, iovcnt, offset); + s64 result = file->device->preadv(iov, iovcnt, offset); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } const s64 pos = file->f.Tell(); @@ -523,8 +742,8 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off file->f.Seek(pos); }; if (!file->f.Seek(offset)) { - LOG_CRITICAL(Kernel_Fs, "failed to seek"); - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EIO; + return -1; } size_t total_read = 0; for (int i = 0; i < iovcnt; i++) { @@ -533,118 +752,72 @@ s64 PS4_SYSV_ABI sceKernelPreadv(int d, SceKernelIovec* iov, int iovcnt, s64 off return total_read; } -s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset) { - SceKernelIovec iovec{buf, nbytes}; - return sceKernelPreadv(d, &iovec, 1, offset); -} - -int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) { - LOG_INFO(Kernel_Fs, "(PARTIAL) fd = {}", fd); - if (fd < 3) { - return ORBIS_KERNEL_ERROR_EPERM; - } - if (sb == nullptr) { - return ORBIS_KERNEL_ERROR_EFAULT; - } - auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(fd); - if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; - } - std::memset(sb, 0, sizeof(OrbisKernelStat)); - - switch (file->type) { - case Core::FileSys::FileType::Device: - return file->device->fstat(sb); - case Core::FileSys::FileType::Regular: - sb->st_mode = 0000777u | 0100000u; - sb->st_size = file->f.GetSize(); - sb->st_blksize = 512; - sb->st_blocks = (sb->st_size + 511) / 512; - // TODO incomplete - break; - case Core::FileSys::FileType::Directory: - sb->st_mode = 0000777u | 0040000u; - sb->st_size = 0; - sb->st_blksize = 512; - sb->st_blocks = 0; - // TODO incomplete - break; - default: - UNREACHABLE(); - } - return ORBIS_OK; -} - -int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) { - int result = sceKernelFStat(fd, sb); +s64 PS4_SYSV_ABI sceKernelPreadv(s32 fd, SceKernelIovec* iov, s32 iovcnt, s64 offset) { + s64 result = posix_preadv(fd, iov, iovcnt, offset); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_fstat: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -s32 PS4_SYSV_ABI sceKernelFsync(int fd) { +s64 PS4_SYSV_ABI posix_pread(s32 fd, void* buf, size_t nbytes, s64 offset) { + SceKernelIovec iovec{buf, nbytes}; + return posix_preadv(fd, &iovec, 1, offset); +} + +s64 PS4_SYSV_ABI sceKernelPread(s32 fd, void* buf, size_t nbytes, s64 offset) { + SceKernelIovec iovec{buf, nbytes}; + return sceKernelPreadv(fd, &iovec, 1, offset); +} + +s32 PS4_SYSV_ABI posix_fsync(s32 fd) { auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } if (file->type == Core::FileSys::FileType::Device) { - return file->device->fsync(); + s32 result = file->device->fsync(); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } file->f.Flush(); return ORBIS_OK; } -s32 PS4_SYSV_ABI posix_fsync(int fd) { - s32 result = sceKernelFsync(fd); +s32 PS4_SYSV_ABI sceKernelFsync(s32 fd) { + s32 result = posix_fsync(fd); if (result < 0) { - LOG_ERROR(Kernel_Pthread, "posix_fsync: error = {}", result); - ErrSceToPosix(result); - return -1; + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } return result; } -int PS4_SYSV_ABI sceKernelFtruncate(int fd, s64 length) { - auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(fd); - - if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; - } - - if (file->type == Core::FileSys::FileType::Device) { - return file->device->ftruncate(length); - } - - if (file->m_host_name.empty()) { - return ORBIS_KERNEL_ERROR_EACCES; - } - - file->f.SetSize(length); - return ORBIS_OK; -} - -static int GetDents(int fd, char* buf, int nbytes, s64* basep) { - if (fd < 3) { - return ORBIS_KERNEL_ERROR_EBADF; - } - +static s32 GetDents(s32 fd, char* buf, s32 nbytes, s64* basep) { if (buf == nullptr) { - return ORBIS_KERNEL_ERROR_EFAULT; + *__Error() = POSIX_EFAULT; + return -1; } auto* h = Common::Singleton::Instance(); auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } if (file->type == Core::FileSys::FileType::Device) { - return file->device->getdents(buf, nbytes, basep); + s32 result = file->device->getdents(buf, nbytes, basep); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } if (file->dirents_index == file->dirents.size()) { @@ -652,7 +825,8 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { } if (file->type != Core::FileSys::FileType::Directory || nbytes < 512 || file->dirents_index > file->dirents.size()) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EINVAL; + return -1; } const auto& entry = file->dirents.at(file->dirents_index++); auto str = entry.name; @@ -673,118 +847,178 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) { return sizeof(OrbisKernelDirent); } -int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) { +s32 PS4_SYSV_ABI posix_getdents(s32 fd, char* buf, s32 nbytes) { return GetDents(fd, buf, nbytes, nullptr); } -int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) { +s32 PS4_SYSV_ABI sceKernelGetdents(s32 fd, char* buf, s32 nbytes) { + s32 result = GetDents(fd, buf, nbytes, nullptr); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI getdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { return GetDents(fd, buf, nbytes, basep); } -s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) { - if (d < 3) { - return ORBIS_KERNEL_ERROR_EPERM; +s32 PS4_SYSV_ABI posix_getdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { + return GetDents(fd, buf, nbytes, basep); +} + +s32 PS4_SYSV_ABI sceKernelGetdirentries(s32 fd, char* buf, s32 nbytes, s64* basep) { + s32 result = GetDents(fd, buf, nbytes, basep); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); } + return result; +} + +s64 PS4_SYSV_ABI posix_pwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { if (offset < 0) { - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EINVAL; + return -1; } auto* h = Common::Singleton::Instance(); - auto* file = h->GetFile(d); + auto* file = h->GetFile(fd); if (file == nullptr) { - return ORBIS_KERNEL_ERROR_EBADF; + *__Error() = POSIX_EBADF; + return -1; } std::scoped_lock lk{file->m_mutex}; if (file->type == Core::FileSys::FileType::Device) { - return file->device->pwrite(buf, nbytes, offset); + s64 result = file->device->pwrite(buf, nbytes, offset); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; } const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); }; if (!file->f.Seek(offset)) { - LOG_CRITICAL(Kernel_Fs, "sceKernelPwrite: failed to seek"); - return ORBIS_KERNEL_ERROR_EINVAL; + *__Error() = POSIX_EIO; + return -1; } return file->f.WriteRaw(buf, nbytes); } -s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) { +s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset) { + s64 result = posix_pwrite(fd, buf, nbytes, offset); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} + +s32 PS4_SYSV_ABI posix_unlink(const char* path) { + if (path == nullptr) { + *__Error() = POSIX_EINVAL; + return -1; + } + + auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); + bool ro = false; - const auto src_path = mnt->GetHostPath(from, &ro); - if (!std::filesystem::exists(src_path)) { - return ORBIS_KERNEL_ERROR_ENOENT; + const auto host_path = mnt->GetHostPath(path, &ro); + if (host_path.empty()) { + *__Error() = POSIX_ENOENT; + return -1; } + if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; + *__Error() = POSIX_EROFS; + return -1; } - const auto dst_path = mnt->GetHostPath(to, &ro); - if (ro) { - return ORBIS_KERNEL_ERROR_EROFS; + + if (std::filesystem::is_directory(host_path)) { + *__Error() = POSIX_EPERM; + return -1; } - const bool src_is_dir = std::filesystem::is_directory(src_path); - const bool dst_is_dir = std::filesystem::is_directory(dst_path); - if (src_is_dir && !dst_is_dir) { - return ORBIS_KERNEL_ERROR_ENOTDIR; + + auto* file = h->GetFile(host_path); + if (file == nullptr) { + // File to unlink hasn't been opened, manually open and unlink it. + Common::FS::IOFile file(host_path, Common::FS::FileAccessMode::ReadWrite); + file.Unlink(); + } else { + file->f.Unlink(); } - if (!src_is_dir && dst_is_dir) { - return ORBIS_KERNEL_ERROR_EISDIR; - } - if (dst_is_dir && !std::filesystem::is_empty(dst_path)) { - return ORBIS_KERNEL_ERROR_ENOTEMPTY; - } - std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing); + + LOG_INFO(Kernel_Fs, "Unlinked {}", path); return ORBIS_OK; } -void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { - LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); - LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); - LIB_FUNCTION("wuCroIGjt2g", "libkernel", 1, "libkernel", 1, 1, open); - LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose); - LIB_FUNCTION("bY-PO6JhzhQ", "libkernel", 1, "libkernel", 1, 1, posix_close); - LIB_FUNCTION("bY-PO6JhzhQ", "libScePosix", 1, "libkernel", 1, 1, posix_close); - LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite); +s32 PS4_SYSV_ABI sceKernelUnlink(const char* path) { + s32 result = posix_unlink(path); + if (result < 0) { + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + return result; +} - LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv); - LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev); - LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek); +void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, open); + LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); + LIB_FUNCTION("wuCroIGjt2g", "libkernel", 1, "libkernel", 1, 1, posix_open); + LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen); + LIB_FUNCTION("NNtFaKJbPt0", "libkernel", 1, "libkernel", 1, 1, close); + LIB_FUNCTION("bY-PO6JhzhQ", "libScePosix", 1, "libkernel", 1, 1, posix_close); + LIB_FUNCTION("bY-PO6JhzhQ", "libkernel", 1, "libkernel", 1, 1, posix_close); + LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose); + LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, write); + LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, posix_write); + LIB_FUNCTION("FN4gaPmuFV8", "libkernel", 1, "libkernel", 1, 1, posix_write); + LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite); + LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, readv); + LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, writev); LIB_FUNCTION("Oy6IpwgtYOk", "libScePosix", 1, "libkernel", 1, 1, posix_lseek); + LIB_FUNCTION("Oy6IpwgtYOk", "libkernel", 1, "libkernel", 1, 1, posix_lseek); LIB_FUNCTION("oib76F-12fk", "libkernel", 1, "libkernel", 1, 1, sceKernelLseek); - LIB_FUNCTION("Cg4srZ6TKbU", "libkernel", 1, "libkernel", 1, 1, sceKernelRead); + LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, read); LIB_FUNCTION("AqBioC2vF3I", "libScePosix", 1, "libkernel", 1, 1, posix_read); - LIB_FUNCTION("1-LFLmRFxxM", "libkernel", 1, "libkernel", 1, 1, sceKernelMkdir); + LIB_FUNCTION("AqBioC2vF3I", "libkernel", 1, "libkernel", 1, 1, posix_read); + LIB_FUNCTION("Cg4srZ6TKbU", "libkernel", 1, "libkernel", 1, 1, sceKernelRead); LIB_FUNCTION("JGMio+21L4c", "libScePosix", 1, "libkernel", 1, 1, posix_mkdir); LIB_FUNCTION("JGMio+21L4c", "libkernel", 1, "libkernel", 1, 1, posix_mkdir); - LIB_FUNCTION("naInUjYt3so", "libkernel", 1, "libkernel", 1, 1, sceKernelRmdir); + LIB_FUNCTION("1-LFLmRFxxM", "libkernel", 1, "libkernel", 1, 1, sceKernelMkdir); LIB_FUNCTION("c7ZnT7V1B98", "libScePosix", 1, "libkernel", 1, 1, posix_rmdir); LIB_FUNCTION("c7ZnT7V1B98", "libkernel", 1, "libkernel", 1, 1, posix_rmdir); - LIB_FUNCTION("eV9wAD2riIA", "libkernel", 1, "libkernel", 1, 1, sceKernelStat); - LIB_FUNCTION("kBwCPsYX-m4", "libkernel", 1, "libkernel", 1, 1, sceKernelFStat); - LIB_FUNCTION("mqQMh1zPPT8", "libScePosix", 1, "libkernel", 1, 1, posix_fstat); - LIB_FUNCTION("mqQMh1zPPT8", "libkernel", 1, "libkernel", 1, 1, posix_fstat); - LIB_FUNCTION("VW3TVZiM4-E", "libkernel", 1, "libkernel", 1, 1, sceKernelFtruncate); - LIB_FUNCTION("52NcYU9+lEo", "libkernel", 1, "libkernel", 1, 1, sceKernelRename); - + LIB_FUNCTION("naInUjYt3so", "libkernel", 1, "libkernel", 1, 1, sceKernelRmdir); LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat); LIB_FUNCTION("E6ao34wPw+U", "libkernel", 1, "libkernel", 1, 1, posix_stat); - LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread); - LIB_FUNCTION("yTj62I7kw4s", "libkernel", 1, "libkernel", 1, 1, sceKernelPreadv); + LIB_FUNCTION("eV9wAD2riIA", "libkernel", 1, "libkernel", 1, 1, sceKernelStat); LIB_FUNCTION("uWyW3v98sU4", "libkernel", 1, "libkernel", 1, 1, sceKernelCheckReachability); - LIB_FUNCTION("fTx66l5iWIA", "libkernel", 1, "libkernel", 1, 1, sceKernelFsync); - LIB_FUNCTION("juWbTNM+8hw", "libkernel", 1, "libkernel", 1, 1, posix_fsync); + LIB_FUNCTION("mqQMh1zPPT8", "libScePosix", 1, "libkernel", 1, 1, posix_fstat); + LIB_FUNCTION("mqQMh1zPPT8", "libkernel", 1, "libkernel", 1, 1, posix_fstat); + LIB_FUNCTION("kBwCPsYX-m4", "libkernel", 1, "libkernel", 1, 1, sceKernelFstat); + LIB_FUNCTION("ih4CD9-gghM", "libkernel", 1, "libkernel", 1, 1, posix_ftruncate); + LIB_FUNCTION("VW3TVZiM4-E", "libkernel", 1, "libkernel", 1, 1, sceKernelFtruncate); + LIB_FUNCTION("52NcYU9+lEo", "libkernel", 1, "libkernel", 1, 1, sceKernelRename); + LIB_FUNCTION("yTj62I7kw4s", "libkernel", 1, "libkernel", 1, 1, sceKernelPreadv); + LIB_FUNCTION("ezv-RSBNKqI", "libScePosix", 1, "libkernel", 1, 1, posix_pread); + LIB_FUNCTION("ezv-RSBNKqI", "libkernel", 1, "libkernel", 1, 1, posix_pread); + LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread); LIB_FUNCTION("juWbTNM+8hw", "libScePosix", 1, "libkernel", 1, 1, posix_fsync); + LIB_FUNCTION("juWbTNM+8hw", "libkernel", 1, "libkernel", 1, 1, posix_fsync); + LIB_FUNCTION("fTx66l5iWIA", "libkernel", 1, "libkernel", 1, 1, sceKernelFsync); LIB_FUNCTION("j2AIqSqJP0w", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdents); + LIB_FUNCTION("sfKygSjIbI8", "libkernel", 1, "libkernel", 1, 1, getdirentries); LIB_FUNCTION("taRWhTJFTgE", "libkernel", 1, "libkernel", 1, 1, sceKernelGetdirentries); + LIB_FUNCTION("C2kJ-byS5rM", "libkernel", 1, "libkernel", 1, 1, posix_pwrite); LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite); LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink); - - // openOrbis (to check if it is valid out of OpenOrbis - LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, - posix_open); // _open should be equal to open function } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/file_system.h b/src/core/libraries/kernel/file_system.h index 1838df2fe..77ce3ec3d 100644 --- a/src/core/libraries/kernel/file_system.h +++ b/src/core/libraries/kernel/file_system.h @@ -65,10 +65,10 @@ constexpr int ORBIS_KERNEL_O_DSYNC = 0x1000; constexpr int ORBIS_KERNEL_O_DIRECT = 0x00010000; constexpr int ORBIS_KERNEL_O_DIRECTORY = 0x00020000; -s64 PS4_SYSV_ABI sceKernelWrite(int d, const void* buf, size_t nbytes); -s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes); -s64 PS4_SYSV_ABI sceKernelPread(int d, void* buf, size_t nbytes, s64 offset); -s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset); +s64 PS4_SYSV_ABI sceKernelWrite(s32 fd, const void* buf, size_t nbytes); +s64 PS4_SYSV_ABI sceKernelRead(s32 fd, void* buf, size_t nbytes); +s64 PS4_SYSV_ABI sceKernelPread(s32 fd, void* buf, size_t nbytes, s64 offset); +s64 PS4_SYSV_ABI sceKernelPwrite(s32 fd, void* buf, size_t nbytes, s64 offset); void RegisterFileSystem(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 2b7735219..9227cf45a 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -85,17 +85,23 @@ int ErrnoToSceKernelError(int error) { } void SetPosixErrno(int e) { - // Some error numbers are different between supported OSes or the PS4 + // Some error numbers are different between supported OSes switch (e) { case EPERM: g_posix_errno = POSIX_EPERM; break; - case EAGAIN: - g_posix_errno = POSIX_EAGAIN; + case ENOENT: + g_posix_errno = POSIX_ENOENT; + break; + case EDEADLK: + g_posix_errno = POSIX_EDEADLK; break; case ENOMEM: g_posix_errno = POSIX_ENOMEM; break; + case EACCES: + g_posix_errno = POSIX_EACCES; + break; case EINVAL: g_posix_errno = POSIX_EINVAL; break; @@ -105,13 +111,14 @@ void SetPosixErrno(int e) { case ERANGE: g_posix_errno = POSIX_ERANGE; break; - case EDEADLK: - g_posix_errno = POSIX_EDEADLK; + case EAGAIN: + g_posix_errno = POSIX_EAGAIN; break; case ETIMEDOUT: g_posix_errno = POSIX_ETIMEDOUT; break; default: + LOG_WARNING(Kernel, "Unhandled errno {}", e); g_posix_errno = e; } } @@ -133,14 +140,6 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) { info->getSegmentInfo = 0; } -s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) { - return sceKernelWrite(d, buf, nbytes); -} - -s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) { - return sceKernelRead(d, buf, nbytes); -} - struct OrbisKernelUuid { u32 timeLow; u16 timeMid; @@ -229,13 +228,10 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Xjoosiw+XPI", "libkernel", 1, "libkernel", 1, 1, sceKernelUuidCreate); LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail); LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error); - LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read); LIB_FUNCTION("k+AXqu2-eBc", "libkernel", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("k+AXqu2-eBc", "libScePosix", 1, "libkernel", 1, 1, posix_getpagesize); LIB_FUNCTION("NWtTN10cJzE", "libSceLibcInternalExt", 1, "libSceLibcInternal", 1, 1, sceLibcHeapGetTraceInfo); - LIB_FUNCTION("FxVZqBAA7ks", "libkernel", 1, "libkernel", 1, 1, ps4__write); - LIB_FUNCTION("FN4gaPmuFV8", "libScePosix", 1, "libkernel", 1, 1, ps4__write); } } // namespace Libraries::Kernel From 69cb4d5787a25c98ec7e89a6c17f0c2acef1b1c8 Mon Sep 17 00:00:00 2001 From: Ked <58560148+k3dr1@users.noreply.github.com> Date: Thu, 27 Mar 2025 00:04:05 +0800 Subject: [PATCH 24/52] Show a dialog only if no game directories are set (#2690) * Slightly changed how allInstallDirsDisabled is determined * Show a dialog only if no game directories are set * Changed a comment * Fixed formatting --- src/qt_gui/main.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 34e429368..bd9dca6ce 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -157,12 +157,8 @@ int main(int argc, char* argv[]) { } } - bool allInstallDirsDisabled = - std::ranges::all_of(Config::getGameInstallDirsEnabled(), [](bool val) { return !val; }); - - // If no game directory is set and no command line argument, prompt for it - if (Config::getGameInstallDirs().empty() && allInstallDirsDisabled && - !has_command_line_argument) { + // If no game directories are set and no command line argument, prompt for it + if (Config::getGameInstallDirsEnabled().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } From 90b949b8ceded997d3066201fe07d6e04fd439fb Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 26 Mar 2025 18:04:49 +0200 Subject: [PATCH 25/52] New Crowdin updates (#2679) * New translations en_us.ts (Albanian) * New translations en_us.ts (Swedish) * New translations en_us.ts (Polish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Spanish) * New translations en_us.ts (Romanian) * New translations en_us.ts (French) * New translations en_us.ts (Arabic) * New translations en_us.ts (Danish) * New translations en_us.ts (German) * New translations en_us.ts (Greek) * New translations en_us.ts (Finnish) * New translations en_us.ts (Hungarian) * New translations en_us.ts (Italian) * New translations en_us.ts (Japanese) * New translations en_us.ts (Korean) * New translations en_us.ts (Lithuanian) * New translations en_us.ts (Dutch) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Russian) * New translations en_us.ts (Turkish) * New translations en_us.ts (Ukrainian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Chinese Traditional) * New translations en_us.ts (Vietnamese) * New translations en_us.ts (Indonesian) * New translations en_us.ts (Persian) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Russian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Swedish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Arabic) * New translations en_us.ts (Lithuanian) * New translations en_us.ts (Arabic) * New translations en_us.ts (Turkish) * New translations en_us.ts (Turkish) * New translations en_us.ts (Arabic) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Albanian) * New translations en_us.ts (Spanish) * New translations en_us.ts (Arabic) * New translations en_us.ts (Arabic) * New translations en_us.ts (Polish) * New translations en_us.ts (Polish) * New translations en_us.ts (Arabic) --- src/qt_gui/translations/ar_SA.ts | 382 ++++++++++++++++--------------- src/qt_gui/translations/da_DK.ts | 12 + src/qt_gui/translations/de_DE.ts | 12 + src/qt_gui/translations/el_GR.ts | 12 + src/qt_gui/translations/es_ES.ts | 12 + src/qt_gui/translations/fa_IR.ts | 12 + src/qt_gui/translations/fi_FI.ts | 12 + src/qt_gui/translations/fr_FR.ts | 12 + src/qt_gui/translations/hu_HU.ts | 12 + src/qt_gui/translations/id_ID.ts | 12 + src/qt_gui/translations/it_IT.ts | 12 + src/qt_gui/translations/ja_JP.ts | 12 + src/qt_gui/translations/ko_KR.ts | 12 + src/qt_gui/translations/lt_LT.ts | 34 ++- src/qt_gui/translations/nb_NO.ts | 12 + src/qt_gui/translations/nl_NL.ts | 12 + src/qt_gui/translations/pl_PL.ts | 166 +++++++------- src/qt_gui/translations/pt_BR.ts | 12 + src/qt_gui/translations/pt_PT.ts | 98 ++++---- src/qt_gui/translations/ro_RO.ts | 12 + src/qt_gui/translations/ru_RU.ts | 12 + src/qt_gui/translations/sq_AL.ts | 12 + src/qt_gui/translations/sv_SE.ts | 12 + src/qt_gui/translations/tr_TR.ts | 12 + src/qt_gui/translations/uk_UA.ts | 12 + src/qt_gui/translations/vi_VN.ts | 12 + src/qt_gui/translations/zh_CN.ts | 12 + src/qt_gui/translations/zh_TW.ts | 12 + 28 files changed, 652 insertions(+), 316 deletions(-) diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index 090cd4c26..ac6920ea0 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -22,7 +22,7 @@ CheatsPatches Cheats / Patches for - Cheats / Patches for + الغِشّ / التصحيحات Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n @@ -407,194 +407,194 @@ ControlSettings Configure Controls - Configure Controls + تعديل عناصر التحكم D-Pad - D-Pad + الأسهم+عصا التحكم Up - Up + فوق Left - Left + يسار Right - Right + يمين Down - Down + تحت Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + مدى تسجيل الإدخال للعصا اليسرى (التلقائي:2 حد أقصى:127) Left Deadzone - Left Deadzone + إعدادات مدى تسجيل الإدخال لعصا التحكم اليسرى Left Stick - Left Stick + عصا التحكم اليسرى Config Selection - Config Selection + تحديد الإعدادات Common Config - Common Config + إعدادات عامة Use per-game configs - Use per-game configs + استخدام إعدادات كل لُعْبَة L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back - Back + رجوع R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + الخيارات / البَدْء R3 - R3 + R3 Face Buttons - Face Buttons + الأزرار Triangle / Y - Triangle / Y + مثلث / Y Square / X - Square / X + مربع / X Circle / B - Circle / B + دائرة / B Cross / A - Cross / A + إكس / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + مدى تسجيل الإدخال للعصا اليمنى (التلقائي:2 حد أقصى:127) Right Deadzone - Right Deadzone + إعدادات مدى تسجيل الإدخال لعصا التحكم اليمنى Right Stick - Right Stick + عصا التحكم اليمنى Color Adjustment - Color Adjustment + تعديل الألوان R: - R: + أحمر: G: - G: + أخضر: B: - B: + أزرق: Override Lightbar Color - Override Lightbar Color + تجاوز لون شريط الإضاءة Override Color - Override Color + تجاوز اللون Unable to Save - Unable to Save + غير قادر على الحفظ Cannot bind axis values more than once - Cannot bind axis values more than once + لا يمكن ربط قيم المحور أكثر من مرة Save - Save + حفظ Apply - Apply + تطبيق Restore Defaults - Restore Defaults + استعادة الإعدادات الافتراضية Cancel - Cancel + إلغاء EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + تحرير أزرار الإدخال للوحة المفاتيح و الفأرة ووحدة التحكم Use Per-Game configs - Use Per-Game configs + استخدام إعدادات كل لُعْبَة Error - Error + خطأ Could not open the file for reading - Could not open the file for reading + تعذر فتح المِلَفّ للقراءة Could not open the file for writing - Could not open the file for writing + تعذر فتح المِلَفّ للكتابة Save Changes - Save Changes + حفظ التغييرات Do you want to save changes? - Do you want to save changes? + هل تريد حفظ التغييرات؟ Help @@ -602,15 +602,15 @@ Do you want to reset your custom default config to the original default config? - Do you want to reset your custom default config to the original default config? + هل تريد إعادة تعيين الإعدادات الافتراضية المخصصة الخاصة بك إلى الإعدادات الافتراضية الأصلية؟ Do you want to reset this config to your custom default config? - Do you want to reset this config to your custom default config? + هل تريد إعادة تعيين هذا الإعداد إلى الإعداد الافتراضي المخصص لك؟ Reset to Default - Reset to Default + إعادة تعيين إلى الافتراضي @@ -702,43 +702,43 @@ Never Played - Never Played + لم تلعب أبداً h - h + ا m - m + ة s - s + ثانية/ثواني Compatibility is untested - Compatibility is untested + التوافق غير مختبر Game does not initialize properly / crashes the emulator - Game does not initialize properly / crashes the emulator + اللعبة لا تهيئ بشكل صحيح / تعطل المحاكي Game boots, but only displays a blank screen - Game boots, but only displays a blank screen + اللعبة تبدأ بالعمل، ولكن فقط تعرض شاشة فارغة Game displays an image but does not go past the menu - Game displays an image but does not go past the menu + اللعبة تعرض صورة ولكن لا تتجاوز القائمة Game has game-breaking glitches or unplayable performance - Game has game-breaking glitches or unplayable performance + اللعبة بها قلتشات أو أداء غير قابل للتشغيل Game can be completed with playable performance and no major glitches - Game can be completed with playable performance and no major glitches + يمكن الانتهاء من اللعبة مع الأداء القابل للتشغيل و لا توجد قلتشات كبيرة Click to see details on github @@ -753,23 +753,23 @@ GameListUtils B - B + بايت KB - KB + كيلو بايت MB - MB + ميغابايت GB - GB + جيجابايت TB - TB + تيرابايت @@ -820,11 +820,11 @@ Copy Version - Copy Version + إصدار النسخة Copy Size - Copy Size + حجم النسخة Copy All @@ -832,39 +832,39 @@ Delete... - Delete... + حذف... Delete Game - Delete Game + حذف اللعبة Delete Update - Delete Update + حذف التحديث Delete DLC - Delete DLC + حذف DLC Delete Trophy - Delete Trophy + حذف الكؤوس Compatibility... - Compatibility... + التوافق... Update database - Update database + تحديث قاعدة البيانات View report - View report + عرض التقرير Submit a report - Submit a report + إرسال بلاغ Shortcut creation @@ -888,94 +888,94 @@ Game - Game + اللعبة This game has no update to delete! - This game has no update to delete! + لا تحتوي اللعبة على تحديث لحذفه! Update - Update + تحديث This game has no DLC to delete! - This game has no DLC to delete! + لا تحتوي اللعبة على DLC لحذفه! DLC - DLC + DLC Delete %1 - Delete %1 + حذف %1 Are you sure you want to delete %1's %2 directory? - Are you sure you want to delete %1's %2 directory? + هل أنت متأكد من أنك تريد حذف دليل %1's %2؟ Open Update Folder - Open Update Folder + فتح مجلد التحديث Delete Save Data - Delete Save Data + حذف التخزينه This game has no update folder to open! - This game has no update folder to open! + لا تحتوي اللعبة على تحديث لفتحه! No log file found for this game! - No log file found for this game! + لم يتم العثور على ملف سجل لهذه اللعبة! Failed to convert icon. - Failed to convert icon. + فشل تحويل الأيقونة. This game has no save data to delete! - This game has no save data to delete! + هذه اللعبة لا تحتوي على أي تخزينات لحذفها! This game has no saved trophies to delete! - This game has no saved trophies to delete! + هذه اللعبة ليس لديها كؤوس محفوظة للحذف! Save Data - Save Data + حفظ البيانات Trophy - Trophy + الكؤوس SFO Viewer for - SFO Viewer for + عارض SFO لـ HelpDialog Quickstart - Quickstart + التشغيل السريع FAQ - FAQ + الأسئلة الأكثر شيوعاً Syntax - Syntax + الصّيغة Special Bindings - Special Bindings + إدخالات خاصة Keybindings - Keybindings + أزرار التحكم @@ -986,178 +986,178 @@ Select which directory you want to install to. - Select which directory you want to install to. + حدد الدليل الذي تريد تثبيت إليه. Install All Queued to Selected Folder - Install All Queued to Selected Folder + تثبيت كل قائمة الانتظار إلى المجلد المحدد Delete PKG File on Install - Delete PKG File on Install + حذف مِلَفّ PKG عند التثبيت KBMSettings Configure Controls - Configure Controls + تعديل عناصر التحكم D-Pad - D-Pad + الأسهم+عصا التحكم Up - Up + أعلى unmapped - unmapped + غير معين Left - Left + يسار Right - Right + يمين Down - Down + أسفل Left Analog Halfmode - Left Analog Halfmode + تقليل سرعة عصا التحكم اليسرى للنصف hold to move left stick at half-speed - hold to move left stick at half-speed + الاستمرار للتحرك إلى اليسار بنصف السرعة Left Stick - Left Stick + عصا التحكم اليسرى Config Selection - Config Selection + تحديد الإعدادات Common Config - Common Config + إعدادات عامة Use per-game configs - Use per-game configs + استخدام إعدادات كل لُعْبَة L1 - L1 + L1 L2 - L2 + L2 Text Editor - Text Editor + محرر النص Help - Help + المساعدة R1 - R1 + R1 R2 - R2 + R2 L3 - L3 + L3 Touchpad Click - Touchpad Click + النقر على لوحة اللمس Mouse to Joystick - Mouse to Joystick + الفأرة إلى عصا التحكم *press F7 ingame to activate - *press F7 ingame to activate + * اضغط على F7 للتفعيل R3 - R3 + R3 Options - Options + الخيارات Mouse Movement Parameters - Mouse Movement Parameters + معطيات حركة الفأرة note: click Help Button/Special Keybindings for more information - note: click Help Button/Special Keybindings for more information + ملاحظة: انقر فوق زر المساعدة/روابط المفاتيح الخاصة للحصول على مزيد من المعلومات Face Buttons - Face Buttons + أزرار الوجه Triangle - Triangle + مثلث Square - Square + مربع Circle - Circle + دائرة Cross - Cross + اكس Right Analog Halfmode - Right Analog Halfmode + تقليل سرعة عصا التحكم اليمنى للنصف hold to move right stick at half-speed - hold to move right stick at half-speed + الضغط باستمرار لتحريك العصا اليمنى بنصف السرعة Right Stick - Right Stick + عصا التحكم اليمنى Speed Offset (def 0.125): - Speed Offset (def 0.125): + إزاحة السرعة (تلقائي 0.125): Copy from Common Config - Copy from Common Config + نسخ من الإعدادات الشائعة Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + Speed Multiplier (def 1.0): - Speed Multiplier (def 1.0): + معدل مضاعفة السرعة (التلقائي 1.0): Common Config Selected - Common Config Selected + الإعدادات الشائعة محدده This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. @@ -1165,7 +1165,7 @@ Copy values from Common Config - Copy values from Common Config + نسخ من الإعدادات الشائعة Do you want to overwrite existing mappings with the mappings from the Common Config? @@ -1173,39 +1173,39 @@ Unable to Save - Unable to Save + غير قادر على الحفظ Cannot bind any unique input more than once - Cannot bind any unique input more than once + لا يمكن ربط أي إدخال فريد أكثر من مرة Press a key - Press a key + اضغط على مفتاح Cannot set mapping - Cannot set mapping + لا يمكن تعيين الأزرار Mousewheel cannot be mapped to stick outputs - Mousewheel cannot be mapped to stick outputs + عجلة الفأرة لا يمكن تعيينها لعصا التحكم Save - Save + حفظ Apply - Apply + تطبيق Restore Defaults - Restore Defaults + استعادة الإعدادات الافتراضية Cancel - Cancel + إلغاء @@ -1244,7 +1244,7 @@ Open shadPS4 Folder - Open shadPS4 Folder + فتح مجلد shadPS4 Exit @@ -1306,6 +1306,14 @@ Dump Game List تفريغ قائمة الألعاب + + Trophy Viewer + عارض الجوائز + + + No games found. Please add your games to your library first. + لم يتم العثور على ألعاب. الرجاء إضافة ألعابك إلى مكتبتك أولاً. + PKG Viewer عارض PKG @@ -1484,27 +1492,27 @@ Run Game - Run Game + تشغيل اللعبة Eboot.bin file not found - Eboot.bin file not found + لم يتم العثور على ملف Eboot.bin PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) + ملف PKG (*.PKG *.pkg) PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! + PKG هو تصحيح أو DLC، يرجى تثبيت اللعبة أولاً! Game is already running! - Game is already running! + اللعبة قيد التشغيل بالفعل! shadPS4 - shadPS4 + shadPS4 @@ -1527,7 +1535,7 @@ Installed - Installed + مثبت Size @@ -1535,15 +1543,15 @@ Category - Category + الفئة Type - Type + النوع App Ver - App Ver + إصدار FW @@ -1630,7 +1638,7 @@ Trophy - Trophy + الكؤوس Open the custom trophy images/sounds folder @@ -2094,23 +2102,23 @@ Display Mode - Display Mode + طريقة العرض Windowed - Windowed + نافذة Fullscreen - Fullscreen + شاشة كاملة Fullscreen (Borderless) - Fullscreen (Borderless) + شاشة كاملة (دون حدود) Window Size - Window Size + حجم النافذة W: @@ -2122,7 +2130,7 @@ Separate Log Files - Separate Log Files + ملفات السجل المنفصل Separate Log Files:\nWrites a separate logfile for each game. @@ -2130,35 +2138,35 @@ Trophy Notification Position - Trophy Notification Position + موقع إشعار الكأس Left - Left + يسار Right - Right + يمين Top - Top + في الأعلى Bottom - Bottom + الأسفل Notification Duration - Notification Duration + مدة الإشعار Portable User Folder - Portable User Folder + مجلد المستخدم المتنقل Create Portable User Folder from Common User Folder - Create Portable User Folder from Common User Folder + إنشاء مجلد مستخدم المتنقل من مجلد المستخدم الشائع Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. @@ -2166,11 +2174,11 @@ Cannot create portable user folder - Cannot create portable user folder + لا يمكن إنشاء مجلد المستخدم المتنقل %1 already exists - %1 already exists + %1 موجود مسبقاً Portable user folder created @@ -2178,7 +2186,7 @@ %1 successfully created. - %1 successfully created. + تم إنشاء %1 بنجاح. Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. @@ -2191,21 +2199,25 @@ Trophy Viewer عارض الجوائز + + Select Game: + اختر الُعْبَه: + Progress - Progress + مقدار التقدُّم Show Earned Trophies - Show Earned Trophies + عرض الكؤوس المكتسبة Show Not Earned Trophies - Show Not Earned Trophies + عرض الكؤوس غير المكتسبة Show Hidden Trophies - Show Hidden Trophies + عرض الكؤوس المخفية diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 113d13019..1835ba84c 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index 7f395c1c8..6717a93ef 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -1306,6 +1306,14 @@ Dump Game List Spielliste ausgeben + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG-Anschauer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophäenansicht + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index a61d84022..6e1adaac9 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index be3c701a9..24844d0a2 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1306,6 +1306,14 @@ Dump Game List Volcar Lista de Juegos + + Trophy Viewer + Expositor de Trofeos + + + No games found. Please add your games to your library first. + No se encontraron juegos. Por favor, añade tus juegos a tu biblioteca primero. + PKG Viewer Vista PKG @@ -2191,6 +2199,10 @@ Trophy Viewer Expositor de Trofeos + + Select Game: + Selecciona un Juego: + Progress Progreso diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 6984b29f8..6b7af042e 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1306,6 +1306,14 @@ Dump Game List استخراج لیست بازی ها + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG مشاهده گر @@ -2191,6 +2199,10 @@ Trophy Viewer مشاهده جوایز + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index 81274ae80..324cb6c49 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -1306,6 +1306,14 @@ Dump Game List Kirjoita Pelilista Tiedostoon + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Selain @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Selain + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index ff1646f9e..ec3f9f8b5 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -1306,6 +1306,14 @@ Dump Game List Dumper la liste des jeux + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer Visionneuse PKG @@ -2191,6 +2199,10 @@ Trophy Viewer Visionneuse de trophées + + Select Game: + Select Game: + Progress Progression diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index c22d74257..6672337a6 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1306,6 +1306,14 @@ Dump Game List Játéklista Dumpolása + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Nézegető @@ -2191,6 +2199,10 @@ Trophy Viewer Trófeák Megtekintése + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index 1a8b085cf..e43d31976 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index 5f57efca3..e63da05b8 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -1306,6 +1306,14 @@ Dump Game List Scarica Lista Giochi + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer Visualizzatore PKG @@ -2191,6 +2199,10 @@ Trophy Viewer Visualizzatore Trofei + + Select Game: + Select Game: + Progress Progresso diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index d93e36770..7cf9fc5c2 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1306,6 +1306,14 @@ Dump Game List ゲームリストをダンプ + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKGビューアー @@ -2191,6 +2199,10 @@ Trophy Viewer トロフィービューアー + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index dc5b61038..d5289ace9 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 2f4b6e59b..17133da35 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -7,7 +7,7 @@ AboutDialog About shadPS4 - About shadPS4 + Apie shadPS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. @@ -15,7 +15,7 @@ This software should not be used to play games you have not legally obtained. - This software should not be used to play games you have not legally obtained. + Ši programa neturėtų būti naudojama žaidimams kurių neturite legaliai įsigiję. @@ -463,7 +463,7 @@ Back - Back + Atgal R1 / RB @@ -519,7 +519,7 @@ Color Adjustment - Color Adjustment + Spalvų Reguliavimas R: @@ -543,7 +543,7 @@ Unable to Save - Unable to Save + Nepavyko Išsaugoti Cannot bind axis values more than once @@ -563,7 +563,7 @@ Cancel - Cancel + Atšaukti @@ -590,15 +590,15 @@ Save Changes - Save Changes + Išsaugoti Pakeitimus Do you want to save changes? - Do you want to save changes? + Ar norite išsaugoti pakeitimus? Help - Help + Pagalba Do you want to reset your custom default config to the original default config? @@ -876,7 +876,7 @@ Error - Error + Klaida Error creating shortcut! @@ -888,7 +888,7 @@ Game - Game + Žaidimas This game has no update to delete! @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index b257b548b..e8ce99f90 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump spilliste + + Trophy Viewer + Troféviser + + + No games found. Please add your games to your library first. + Fant ingen spill. Legg til spillene dine i biblioteket først. + PKG Viewer PKG-viser @@ -2191,6 +2199,10 @@ Trophy Viewer Troféviser + + Select Game: + Velg spill: + Progress Fremdrift diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index f6c062da3..5c1725bd5 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 4c4a33ec2..033412efa 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -543,38 +543,38 @@ Unable to Save - Unable to Save + Zapisywanie nie powiodło się Cannot bind axis values more than once - Cannot bind axis values more than once + Nie można powiązać wartości osi więcej niż raz Save - Save + Zapisz Apply - Apply + Zastosuj Restore Defaults - Restore Defaults + Przywróć ustawienia domyślne Cancel - Cancel + Anuluj EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Edytuj przypisanie klawiszy klawiatury + myszy oraz kontrolera Use Per-Game configs - Use Per-Game configs + Użyj osobnej konfiguracji dla każdej gry Error @@ -582,11 +582,11 @@ Could not open the file for reading - Could not open the file for reading + Nie można otworzyć pliku do odczytu Could not open the file for writing - Could not open the file for writing + Nie można otworzyć pliku do zapisu Save Changes @@ -602,11 +602,11 @@ Do you want to reset your custom default config to the original default config? - Do you want to reset your custom default config to the original default config? + Czy chcesz zresetować Twoją domyślną konfigurację do oryginalnej domyślnej konfiguracji? Do you want to reset this config to your custom default config? - Do you want to reset this config to your custom default config? + Czy chcesz zresetować tę konfigurację do Twojej domyślnej konfiguracji? Reset to Default @@ -959,23 +959,23 @@ HelpDialog Quickstart - Quickstart + Szybki start FAQ - FAQ + Najczęściej zadawane pytania Syntax - Syntax + Składnia Special Bindings - Special Bindings + Specjalne wiązania Keybindings - Keybindings + Przypisanie klawiszy @@ -1001,211 +1001,211 @@ KBMSettings Configure Controls - Configure Controls + Skonfiguruj sterowanie D-Pad - D-Pad + Krzyżak Up - Up + Strzałka w górę unmapped - unmapped + nieprzypisane Left - Left + Strzałka w lewo Right - Right + Strzałka w prawo Down - Down + Strzałka w dół Left Analog Halfmode - Left Analog Halfmode + Połowiczny tryb lewego drążka hold to move left stick at half-speed - hold to move left stick at half-speed + przytrzymaj, aby przesuwać lewy drążek dwa razy wolniej Left Stick - Left Stick + Lewy drążek Config Selection - Config Selection + Wybór konfiguracji Common Config - Common Config + Typowa konfiguracja Use per-game configs - Use per-game configs + Użyj osobnej konfiguracji dla każdej gry L1 - L1 + L1 L2 - L2 + L2 Text Editor - Text Editor + Edytor tekstu Help - Help + Pomoc R1 - R1 + R1 R2 - R2 + R2 L3 - L3 + L3 Touchpad Click - Touchpad Click + Kliknięcie Touchpada Mouse to Joystick - Mouse to Joystick + Mysz na Joystick *press F7 ingame to activate - *press F7 ingame to activate + *naciśnij F7 w grze aby aktywować R3 - R3 + R3 Options - Options + Opcje Mouse Movement Parameters - Mouse Movement Parameters + Parametry ruchu myszy note: click Help Button/Special Keybindings for more information - note: click Help Button/Special Keybindings for more information + uwaga: kliknij przycisk Pomoc/Specjalne skróty klawiszowe, aby uzyskać więcej informacji Face Buttons - Face Buttons + Przednie przyciski Triangle - Triangle + Trójkąt Square - Square + Kwadrat Circle - Circle + Kółko Cross - Cross + Krzyżyk Right Analog Halfmode - Right Analog Halfmode + Połowiczny tryb prawego drążka hold to move right stick at half-speed - hold to move right stick at half-speed + przytrzymaj, aby przesuwać prawy drążek dwa razy wolniej Right Stick - Right Stick + Prawy drążek Speed Offset (def 0.125): - Speed Offset (def 0.125): + Offset prędkości (def 0,125): Copy from Common Config - Copy from Common Config + Kopiuj z typowej konfiguracji Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + Offset martwych stref (def 0,50): Speed Multiplier (def 1.0): - Speed Multiplier (def 1.0): + Mnożnik prędkości (def1.0): Common Config Selected - Common Config Selected + Wybrano typową konfigurację This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Przycisk ten kopiuje mapowanie z typowej konfiguracji do aktualnie wybranego profilu, i nie może być użyty, gdy aktualnie wybranym profilem jest typowa konfiguracja. Copy values from Common Config - Copy values from Common Config + Kopiuj z typowej konfiguracji Do you want to overwrite existing mappings with the mappings from the Common Config? - Do you want to overwrite existing mappings with the mappings from the Common Config? + Czy chcesz nadpisać istniejące mapowania mapowaniem z typowej konfiguracji? Unable to Save - Unable to Save + Zapisywanie nie powiodło się Cannot bind any unique input more than once - Cannot bind any unique input more than once + Nie można powiązać żadnych unikalnych danych wejściowych więcej niż raz Press a key - Press a key + Naciśnij klawisz Cannot set mapping - Cannot set mapping + Nie można ustawić mapowania Mousewheel cannot be mapped to stick outputs - Mousewheel cannot be mapped to stick outputs + Kółko myszy nie może być przypisane do sterowania drążkiem Save - Save + Zapisz Apply - Apply + Zastosuj Restore Defaults - Restore Defaults + Przywróć ustawienia domyślne Cancel - Cancel + Anuluj @@ -1306,6 +1306,14 @@ Dump Game List Zgraj listę gier + + Trophy Viewer + Menedżer trofeów + + + No games found. Please add your games to your library first. + Nie znaleziono gier. Najpierw dodaj swoje gry do swojej biblioteki. + PKG Viewer Menedżer plików PKG @@ -2154,35 +2162,35 @@ Portable User Folder - Portable User Folder + Przenośny folder użytkownika Create Portable User Folder from Common User Folder - Create Portable User Folder from Common User Folder + Utwórz przenośny folder użytkownika ze zwykłego folderu użytkownika Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. - Portable user folder:\nStores shadPS4 settings and data that will be applied only to the shadPS4 build located in the current folder. Restart the app after creating the portable user folder to begin using it. + Przenośny folder użytkownika:\nPrzechowuje ustawienia shadPS4 i dane, które zostaną zastosowane tylko do kompilacji shadPS4 znajdującej się w bieżącym folderze. Uruchom ponownie aplikację po utworzeniu przenośnego folderu użytkownika, aby zacząć z niego korzystać. Cannot create portable user folder - Cannot create portable user folder + Nie można utworzyć przenośnego folderu użytkownika %1 already exists - %1 already exists + %1 już istnieje Portable user folder created - Portable user folder created + Utworzono przenośny folder użytkownika %1 successfully created. - %1 successfully created. + %1 prawidłowo utworzony. Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Otwórz niestandardowy folder obrazów/dźwięków:\nMożesz dodać własne obrazy dla trofeów i ich dźwięki.\nDodaj pliki do custom_trophy o następujących nazwach:\ntrophy.wav LUB trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nUwaga: Dźwięki działają tylko w wersji QT. @@ -2191,6 +2199,10 @@ Trophy Viewer Menedżer trofeów + + Select Game: + Wybierz grę: + Progress Postęp diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index e37a3fe96..d44efce5d 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -1306,6 +1306,14 @@ Dump Game List Exportar Lista de Jogos + + Trophy Viewer + Visualizador de Troféus + + + No games found. Please add your games to your library first. + Nenhum jogo encontrado. Adicione seus jogos à sua biblioteca primeiro. + PKG Viewer Visualizador de PKG @@ -2191,6 +2199,10 @@ Trophy Viewer Visualizador de Troféus + + Select Game: + Selecionar Jogo: + Progress Progresso diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index dc0059b86..455955fad 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -543,74 +543,74 @@ Unable to Save - Unable to Save + Não é possível salvar Cannot bind axis values more than once - Cannot bind axis values more than once + Não foi possível atribuir os valores do eixo X ou Y mais de uma vez Save - Save + Salvar Apply - Apply + Aplicar Restore Defaults - Restore Defaults + Restaurar o Padrão Cancel - Cancel + Cancelar EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Editar comandos do Teclado + Mouse e do Controle Use Per-Game configs - Use Per-Game configs + Use uma configuração para cada jogo Error - Error + Erro Could not open the file for reading - Could not open the file for reading + Não foi possível abrir o arquivo para ler Could not open the file for writing - Could not open the file for writing + Não foi possível abrir o arquivo para escrever Save Changes - Save Changes + Salvar mudanças Do you want to save changes? - Do you want to save changes? + Salvar as mudanças? Help - Help + Ajuda Do you want to reset your custom default config to the original default config? - Do you want to reset your custom default config to the original default config? + Restaurar a configuração customizada padrão para a configuração original padrão? Do you want to reset this config to your custom default config? - Do you want to reset this config to your custom default config? + Deseja redefinir esta configuração para a configuração padrão personalizada? Reset to Default - Reset to Default + Resetar ao Padrão @@ -959,23 +959,23 @@ HelpDialog Quickstart - Quickstart + Início Rápido FAQ - FAQ + Perguntas Frequentes Syntax - Syntax + Sintaxe Special Bindings - Special Bindings + Atalhos Especiais Keybindings - Keybindings + Combinações de Teclas @@ -1145,67 +1145,67 @@ Copy from Common Config - Copy from Common Config + Copiar da Configuração Comum Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + Deslocamento da Zona Morta (def 0,50): Speed Multiplier (def 1.0): - Speed Multiplier (def 1.0): + Multiplicador de Velocidade (def 1,0): Common Config Selected - Common Config Selected + Configuração Comum Selecionada This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + Este botão copia mapeamentos da Configuração Comum para o perfil atualmente selecionado, e não pode ser usado quando o perfil atualmente selecionado é a Configuração Comum. Copy values from Common Config - Copy values from Common Config + Copiar valores da Configuração Comum Do you want to overwrite existing mappings with the mappings from the Common Config? - Do you want to overwrite existing mappings with the mappings from the Common Config? + Substituir mapeamentos existentes com os mapeamentos da Configuração Comum? Unable to Save - Unable to Save + Não é possível salvar Cannot bind any unique input more than once - Cannot bind any unique input more than once + Não é possível vincular qualquer entrada única mais de uma vez Press a key - Press a key + Pressione uma tecla Cannot set mapping - Cannot set mapping + Não é possível definir o mapeamento Mousewheel cannot be mapped to stick outputs - Mousewheel cannot be mapped to stick outputs + Roda do rato não pode ser mapeada para saídas empates Save - Save + Salvar Apply - Apply + Aplicar Restore Defaults - Restore Defaults + Restaurar Definições Cancel - Cancel + Cancelar @@ -1306,6 +1306,14 @@ Dump Game List Exportar Lista de Jogos + + Trophy Viewer + Visualizador de Troféus + + + No games found. Please add your games to your library first. + Nenhum jogo encontrado. Por favor, adicione os seus jogos à sua biblioteca primeiro. + PKG Viewer Visualizador PKG @@ -2166,23 +2174,23 @@ Cannot create portable user folder - Cannot create portable user folder + Não é possível criar pasta de utilizador portátil %1 already exists - %1 already exists + %1 já existe Portable user folder created - Portable user folder created + Pasta de utilizador portátil criada %1 successfully created. - %1 successfully created. + %1 criado com sucesso. Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. - Open the custom trophy images/sounds folder:\nYou can add custom images to the trophies and an audio.\nAdd the files to custom_trophy with the following names:\ntrophy.wav OR trophy.mp3, bronze.png, gold.png, platinum.png, silver.png\nNote: The sound will only work in QT versions. + Abra a pasta de imagens/sons de troféus personalizados:\nPoderá adicionar imagens personalizadas aos troféus e um áudio.\nAdicione os arquivos na pasta custom_trophy com os seguintes nomes:\ntrophy.mp3 ou trophy.wav, bronze.png, gold.png, platinum.png, silver.png\nObservação: O som funcionará apenas nas versões Qt. @@ -2191,6 +2199,10 @@ Trophy Viewer Visualizador de Troféus + + Select Game: + Escolha o Jogo: + Progress Progresso diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 4261bf9e2..9c7720e17 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 6ca16121f..68eaabc34 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1306,6 +1306,14 @@ Dump Game List Дамп списка игр + + Trophy Viewer + Просмотр трофеев + + + No games found. Please add your games to your library first. + Не найдено ни одной игры. Пожалуйста, сначала добавьте игры в библиотеку. + PKG Viewer Просмотр PKG @@ -2191,6 +2199,10 @@ Trophy Viewer Просмотр трофеев + + Select Game: + Выберите игру: + Progress Прогресс diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index bf58dc60d..657f78d0d 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -1306,6 +1306,14 @@ Dump Game List Zbraz Listën e Lojërave + + Trophy Viewer + Shikuesi i Trofeve + + + No games found. Please add your games to your library first. + Nuk u gjetën lojëra. Shto lojërat në librarinë tënde fillimisht. + PKG Viewer Shikuesi i PKG @@ -2191,6 +2199,10 @@ Trophy Viewer Shikuesi i Trofeve + + Select Game: + Zgjidh Lojën: + Progress Ecuria diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index d631859d4..a002b150a 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -1306,6 +1306,14 @@ Dump Game List Dumpa spellista + + Trophy Viewer + Trofévisare + + + No games found. Please add your games to your library first. + Inga spel hittades. Lägg till dina spel till biblioteket först. + PKG Viewer PKG-visare @@ -2191,6 +2199,10 @@ Trophy Viewer Trofé-visare + + Select Game: + Välj spel: + Progress Förlopp diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 394704274..48ce4254d 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1306,6 +1306,14 @@ Dump Game List Oyun Listesini Kaydet + + Trophy Viewer + Kupa Görüntüleyici + + + No games found. Please add your games to your library first. + Oyun bulunamadı. Oyunlarınızı lütfen önce kütüphanenize ekleyin. + PKG Viewer PKG Görüntüleyici @@ -2191,6 +2199,10 @@ Trophy Viewer Kupa Görüntüleyici + + Select Game: + Oyun Seç: + Progress İlerleme diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 6083577fa..06c88428b 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -1306,6 +1306,14 @@ Dump Game List Дамп списку ігор + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer Перегляд PKG @@ -2191,6 +2199,10 @@ Trophy Viewer Трофеї + + Select Game: + Select Game: + Progress Прогрес diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 8fa0889bc..c16604b85 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trình xem chiến tích + + Select Game: + Select Game: + Progress Progress diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 6e1fcdc95..6364ae1d6 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1306,6 +1306,14 @@ Dump Game List 导出游戏列表 + + Trophy Viewer + 奖杯查看器 + + + No games found. Please add your games to your library first. + 未找到游戏。请先将您的游戏添加到您的资料库。 + PKG Viewer PKG 查看器 @@ -2191,6 +2199,10 @@ Trophy Viewer 奖杯查看器 + + Select Game: + 选择游戏: + Progress 进度 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 2950f541f..bd546380a 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1306,6 +1306,14 @@ Dump Game List Dump Game List + + Trophy Viewer + Trophy Viewer + + + No games found. Please add your games to your library first. + No games found. Please add your games to your library first. + PKG Viewer PKG Viewer @@ -2191,6 +2199,10 @@ Trophy Viewer Trophy Viewer + + Select Game: + Select Game: + Progress Progress From 3abe5b0d5774e2d7b959d85f51657144b3d822c7 Mon Sep 17 00:00:00 2001 From: Lizardy <6063922+lzardy@users.noreply.github.com> Date: Wed, 26 Mar 2025 16:42:49 +0000 Subject: [PATCH 26/52] [Libs] Ngs2 (#1604) * [libSceNgs2] Logging & Structs * clang * clang * stdarg incl * proper logs * ngs2 latest * [libSceNgs2] Logging & Structs * clang * latest * fix includes * clang * clang --------- Co-authored-by: microsoftv <6063922+microsoftv@users.noreply.github.com> --- CMakeLists.txt | 18 + src/core/libraries/ngs2/ngs2.cpp | 682 ++++++++++++++------- src/core/libraries/ngs2/ngs2.h | 283 +++++++-- src/core/libraries/ngs2/ngs2_custom.cpp | 12 + src/core/libraries/ngs2/ngs2_custom.h | 444 ++++++++++++++ src/core/libraries/ngs2/ngs2_eq.cpp | 12 + src/core/libraries/ngs2/ngs2_eq.h | 41 ++ src/core/libraries/ngs2/ngs2_geom.cpp | 12 + src/core/libraries/ngs2/ngs2_geom.h | 80 +++ src/core/libraries/ngs2/ngs2_impl.cpp | 208 ++++--- src/core/libraries/ngs2/ngs2_impl.h | 179 +++++- src/core/libraries/ngs2/ngs2_mastering.cpp | 12 + src/core/libraries/ngs2/ngs2_mastering.h | 81 +++ src/core/libraries/ngs2/ngs2_pan.cpp | 12 + src/core/libraries/ngs2/ngs2_pan.h | 25 + src/core/libraries/ngs2/ngs2_report.cpp | 12 + src/core/libraries/ngs2/ngs2_report.h | 78 +++ src/core/libraries/ngs2/ngs2_reverb.cpp | 12 + src/core/libraries/ngs2/ngs2_reverb.h | 61 ++ src/core/libraries/ngs2/ngs2_sampler.cpp | 12 + src/core/libraries/ngs2/ngs2_sampler.h | 162 +++++ src/core/libraries/ngs2/ngs2_submixer.cpp | 12 + src/core/libraries/ngs2/ngs2_submixer.h | 126 ++++ 23 files changed, 2206 insertions(+), 370 deletions(-) create mode 100644 src/core/libraries/ngs2/ngs2_custom.cpp create mode 100644 src/core/libraries/ngs2/ngs2_custom.h create mode 100644 src/core/libraries/ngs2/ngs2_eq.cpp create mode 100644 src/core/libraries/ngs2/ngs2_eq.h create mode 100644 src/core/libraries/ngs2/ngs2_geom.cpp create mode 100644 src/core/libraries/ngs2/ngs2_geom.h create mode 100644 src/core/libraries/ngs2/ngs2_mastering.cpp create mode 100644 src/core/libraries/ngs2/ngs2_mastering.h create mode 100644 src/core/libraries/ngs2/ngs2_pan.cpp create mode 100644 src/core/libraries/ngs2/ngs2_pan.h create mode 100644 src/core/libraries/ngs2/ngs2_report.cpp create mode 100644 src/core/libraries/ngs2/ngs2_report.h create mode 100644 src/core/libraries/ngs2/ngs2_reverb.cpp create mode 100644 src/core/libraries/ngs2/ngs2_reverb.h create mode 100644 src/core/libraries/ngs2/ngs2_sampler.cpp create mode 100644 src/core/libraries/ngs2/ngs2_sampler.h create mode 100644 src/core/libraries/ngs2/ngs2_submixer.cpp create mode 100644 src/core/libraries/ngs2/ngs2_submixer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 185205221..1c0932b5c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -378,6 +378,24 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/ngs2/ngs2_error.h src/core/libraries/ngs2/ngs2_impl.cpp src/core/libraries/ngs2/ngs2_impl.h + src/core/libraries/ngs2/ngs2_custom.cpp + src/core/libraries/ngs2/ngs2_custom.h + src/core/libraries/ngs2/ngs2_reverb.cpp + src/core/libraries/ngs2/ngs2_reverb.h + src/core/libraries/ngs2/ngs2_geom.cpp + src/core/libraries/ngs2/ngs2_geom.h + src/core/libraries/ngs2/ngs2_pan.cpp + src/core/libraries/ngs2/ngs2_pan.h + src/core/libraries/ngs2/ngs2_report.cpp + src/core/libraries/ngs2/ngs2_report.h + src/core/libraries/ngs2/ngs2_eq.cpp + src/core/libraries/ngs2/ngs2_eq.h + src/core/libraries/ngs2/ngs2_mastering.cpp + src/core/libraries/ngs2/ngs2_mastering.h + src/core/libraries/ngs2/ngs2_sampler.cpp + src/core/libraries/ngs2/ngs2_sampler.h + src/core/libraries/ngs2/ngs2_submixer.cpp + src/core/libraries/ngs2/ngs2_submixer.h src/core/libraries/ajm/ajm_error.h src/core/libraries/audio3d/audio3d.cpp src/core/libraries/audio3d/audio3d.h diff --git a/src/core/libraries/ngs2/ngs2.cpp b/src/core/libraries/ngs2/ngs2.cpp index 7eb663413..0b42e2471 100644 --- a/src/core/libraries/ngs2/ngs2.cpp +++ b/src/core/libraries/ngs2/ngs2.cpp @@ -5,21 +5,480 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" +#include "core/libraries/ngs2/ngs2_custom.h" #include "core/libraries/ngs2/ngs2_error.h" +#include "core/libraries/ngs2/ngs2_geom.h" #include "core/libraries/ngs2/ngs2_impl.h" +#include "core/libraries/ngs2/ngs2_pan.h" +#include "core/libraries/ngs2/ngs2_report.h" namespace Libraries::Ngs2 { -int PS4_SYSV_ABI sceNgs2CalcWaveformBlock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); +// Ngs2 + +s32 PS4_SYSV_ABI sceNgs2CalcWaveformBlock(const OrbisNgs2WaveformFormat* format, u32 samplePos, + u32 numSamples, OrbisNgs2WaveformBlock* outBlock) { + LOG_INFO(Lib_Ngs2, "samplePos = {}, numSamples = {}", samplePos, numSamples); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2CustomRackGetModuleInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); +s32 PS4_SYSV_ABI sceNgs2GetWaveformFrameInfo(const OrbisNgs2WaveformFormat* format, + u32* outFrameSize, u32* outNumFrameSamples, + u32* outUnitsPerFrame, u32* outNumDelaySamples) { + LOG_INFO(Lib_Ngs2, "called"); return ORBIS_OK; } +s32 PS4_SYSV_ABI sceNgs2ParseWaveformData(const void* data, size_t dataSize, + OrbisNgs2WaveformInfo* outInfo) { + LOG_INFO(Lib_Ngs2, "dataSize = {}", dataSize); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2ParseWaveformFile(const char* path, u64 offset, + OrbisNgs2WaveformInfo* outInfo) { + LOG_INFO(Lib_Ngs2, "path = {}, offset = {}", path, offset); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2ParseWaveformUser(OrbisNgs2ParseReadHandler handler, uintptr_t userData, + OrbisNgs2WaveformInfo* outInfo) { + LOG_INFO(Lib_Ngs2, "userData = {}", userData); + if (!handler) { + LOG_ERROR(Lib_Ngs2, "handler is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackCreate(OrbisNgs2Handle systemHandle, u32 rackId, + const OrbisNgs2RackOption* option, + const OrbisNgs2ContextBufferInfo* bufferInfo, + OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackCreateWithAllocator(OrbisNgs2Handle systemHandle, u32 rackId, + const OrbisNgs2RackOption* option, + const OrbisNgs2BufferAllocator* allocator, + OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackDestroy(OrbisNgs2Handle rackHandle, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackGetInfo(OrbisNgs2Handle rackHandle, OrbisNgs2RackInfo* outInfo, + size_t infoSize) { + LOG_INFO(Lib_Ngs2, "infoSize = {}", infoSize); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackGetUserData(OrbisNgs2Handle rackHandle, uintptr_t* outUserData) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackGetVoiceHandle(OrbisNgs2Handle rackHandle, u32 voiceIndex, + OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "voiceIndex = {}", voiceIndex); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackLock(OrbisNgs2Handle rackHandle) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackQueryBufferSize(u32 rackId, const OrbisNgs2RackOption* option, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + LOG_INFO(Lib_Ngs2, "rackId = {}", rackId); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackSetUserData(OrbisNgs2Handle rackHandle, uintptr_t userData) { + LOG_INFO(Lib_Ngs2, "userData = {}", userData); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2RackUnlock(OrbisNgs2Handle rackHandle) { + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemCreate(const OrbisNgs2SystemOption* option, + const OrbisNgs2ContextBufferInfo* bufferInfo, + OrbisNgs2Handle* outHandle) { + s32 result; + OrbisNgs2ContextBufferInfo localInfo; + if (!bufferInfo || !outHandle) { + if (!bufferInfo) { + result = ORBIS_NGS2_ERROR_INVALID_BUFFER_INFO; + LOG_ERROR(Lib_Ngs2, "Invalid system buffer info {}", (void*)bufferInfo); + } else { + result = ORBIS_NGS2_ERROR_INVALID_OUT_ADDRESS; + LOG_ERROR(Lib_Ngs2, "Invalid system handle address {}", (void*)outHandle); + } + + // TODO: Report errors? + } else { + // Make bufferInfo copy + localInfo.hostBuffer = bufferInfo->hostBuffer; + localInfo.hostBufferSize = bufferInfo->hostBufferSize; + for (int i = 0; i < 5; i++) { + localInfo.reserved[i] = bufferInfo->reserved[i]; + } + localInfo.userData = bufferInfo->userData; + + result = SystemSetup(option, &localInfo, 0, outHandle); + } + + // TODO: API reporting? + + LOG_INFO(Lib_Ngs2, "called"); + return result; +} + +s32 PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator(const OrbisNgs2SystemOption* option, + const OrbisNgs2BufferAllocator* allocator, + OrbisNgs2Handle* outHandle) { + s32 result; + if (allocator && allocator->allocHandler != 0) { + OrbisNgs2BufferAllocHandler hostAlloc = allocator->allocHandler; + if (outHandle) { + OrbisNgs2BufferFreeHandler hostFree = allocator->freeHandler; + OrbisNgs2ContextBufferInfo* bufferInfo = 0; + result = SystemSetup(option, bufferInfo, 0, 0); + if (result >= 0) { + uintptr_t sysUserData = allocator->userData; + result = hostAlloc(bufferInfo); + if (result >= 0) { + OrbisNgs2Handle* handleCopy = outHandle; + result = SystemSetup(option, bufferInfo, hostFree, handleCopy); + if (result < 0) { + if (hostFree) { + hostFree(bufferInfo); + } + } + } + } + } else { + result = ORBIS_NGS2_ERROR_INVALID_OUT_ADDRESS; + LOG_ERROR(Lib_Ngs2, "Invalid system handle address {}", (void*)outHandle); + } + } else { + result = ORBIS_NGS2_ERROR_INVALID_BUFFER_ALLOCATOR; + LOG_ERROR(Lib_Ngs2, "Invalid system buffer allocator {}", (void*)allocator); + } + LOG_INFO(Lib_Ngs2, "called"); + return result; +} + +s32 PS4_SYSV_ABI sceNgs2SystemDestroy(OrbisNgs2Handle systemHandle, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemEnumHandles(OrbisNgs2Handle* aOutHandle, u32 maxHandles) { + LOG_INFO(Lib_Ngs2, "maxHandles = {}", maxHandles); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemEnumRackHandles(OrbisNgs2Handle systemHandle, + OrbisNgs2Handle* aOutHandle, u32 maxHandles) { + LOG_INFO(Lib_Ngs2, "maxHandles = {}", maxHandles); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemGetInfo(OrbisNgs2Handle rackHandle, OrbisNgs2SystemInfo* outInfo, + size_t infoSize) { + LOG_INFO(Lib_Ngs2, "infoSize = {}", infoSize); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemGetUserData(OrbisNgs2Handle systemHandle, uintptr_t* outUserData) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemLock(OrbisNgs2Handle systemHandle) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemQueryBufferSize(const OrbisNgs2SystemOption* option, + OrbisNgs2ContextBufferInfo* outBufferInfo) { + s32 result; + if (outBufferInfo) { + result = SystemSetup(option, outBufferInfo, 0, 0); + LOG_INFO(Lib_Ngs2, "called"); + } else { + result = ORBIS_NGS2_ERROR_INVALID_OUT_ADDRESS; + LOG_ERROR(Lib_Ngs2, "Invalid system buffer info {}", (void*)outBufferInfo); + } + + return result; +} + +s32 PS4_SYSV_ABI sceNgs2SystemRender(OrbisNgs2Handle systemHandle, + const OrbisNgs2RenderBufferInfo* aBufferInfo, + u32 numBufferInfo) { + LOG_INFO(Lib_Ngs2, "numBufferInfo = {}", numBufferInfo); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +static s32 PS4_SYSV_ABI sceNgs2SystemResetOption(OrbisNgs2SystemOption* outOption) { + static const OrbisNgs2SystemOption option = { + sizeof(OrbisNgs2SystemOption), "", 0, 512, 256, 48000, {0}}; + + if (!outOption) { + LOG_ERROR(Lib_Ngs2, "Invalid system option address {}", (void*)outOption); + return ORBIS_NGS2_ERROR_INVALID_OPTION_ADDRESS; + } + *outOption = option; + + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemSetGrainSamples(OrbisNgs2Handle systemHandle, u32 numSamples) { + LOG_INFO(Lib_Ngs2, "numSamples = {}", numSamples); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemSetSampleRate(OrbisNgs2Handle systemHandle, u32 sampleRate) { + LOG_INFO(Lib_Ngs2, "sampleRate = {}", sampleRate); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemSetUserData(OrbisNgs2Handle systemHandle, uintptr_t userData) { + LOG_INFO(Lib_Ngs2, "userData = {}", userData); + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2SystemUnlock(OrbisNgs2Handle systemHandle) { + if (!systemHandle) { + LOG_ERROR(Lib_Ngs2, "systemHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceControl(OrbisNgs2Handle voiceHandle, + const OrbisNgs2VoiceParamHeader* paramList) { + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetMatrixInfo(OrbisNgs2Handle voiceHandle, u32 matrixId, + OrbisNgs2VoiceMatrixInfo* outInfo, size_t outInfoSize) { + LOG_INFO(Lib_Ngs2, "matrixId = {}, outInfoSize = {}", matrixId, outInfoSize); + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetOwner(OrbisNgs2Handle voiceHandle, OrbisNgs2Handle* outRackHandle, + u32* outVoiceId) { + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetPortInfo(OrbisNgs2Handle voiceHandle, u32 port, + OrbisNgs2VoicePortInfo* outInfo, size_t outInfoSize) { + LOG_INFO(Lib_Ngs2, "port = {}, outInfoSize = {}", port, outInfoSize); + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetState(OrbisNgs2Handle voiceHandle, OrbisNgs2VoiceState* outState, + size_t stateSize) { + LOG_INFO(Lib_Ngs2, "stateSize = {}", stateSize); + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2VoiceGetStateFlags(OrbisNgs2Handle voiceHandle, u32* outStateFlags) { + if (!voiceHandle) { + LOG_ERROR(Lib_Ngs2, "voiceHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +// Ngs2Custom + +s32 PS4_SYSV_ABI sceNgs2CustomRackGetModuleInfo(OrbisNgs2Handle rackHandle, u32 moduleIndex, + OrbisNgs2CustomModuleInfo* outInfo, + size_t infoSize) { + LOG_INFO(Lib_Ngs2, "moduleIndex = {}, infoSize = {}", moduleIndex, infoSize); + if (!rackHandle) { + LOG_ERROR(Lib_Ngs2, "rackHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; + } + return ORBIS_OK; +} + +// Ngs2Geom + +s32 PS4_SYSV_ABI sceNgs2GeomResetListenerParam(OrbisNgs2GeomListenerParam* outListenerParam) { + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2GeomResetSourceParam(OrbisNgs2GeomSourceParam* outSourceParam) { + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2GeomCalcListener(const OrbisNgs2GeomListenerParam* param, + OrbisNgs2GeomListenerWork* outWork, u32 flags) { + LOG_INFO(Lib_Ngs2, "flags = {}", flags); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener, + const OrbisNgs2GeomSourceParam* source, + OrbisNgs2GeomAttribute* outAttrib, u32 flags) { + LOG_INFO(Lib_Ngs2, "flags = {}", flags); + return ORBIS_OK; +} + +// Ngs2Pan + +s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle, + u32 numSpeakers) { + LOG_INFO(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle, + unitAngle, numSpeakers); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2PanGetVolumeMatrix(OrbisNgs2PanWork* work, const OrbisNgs2PanParam* aParam, + u32 numParams, u32 matrixFormat, + float* outVolumeMatrix) { + LOG_INFO(Lib_Ngs2, "numParams = {}, matrixFormat = {}", numParams, matrixFormat); + return ORBIS_OK; +} + +// Ngs2Report + +s32 PS4_SYSV_ABI sceNgs2ReportRegisterHandler(u32 reportType, OrbisNgs2ReportHandler handler, + uintptr_t userData, OrbisNgs2Handle* outHandle) { + LOG_INFO(Lib_Ngs2, "reportType = {}, userData = {}", reportType, userData); + if (!handler) { + LOG_ERROR(Lib_Ngs2, "handler is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceNgs2ReportUnregisterHandler(OrbisNgs2Handle reportHandle) { + if (!reportHandle) { + LOG_ERROR(Lib_Ngs2, "reportHandle is nullptr"); + return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; + } + LOG_INFO(Lib_Ngs2, "called"); + return ORBIS_OK; +} + +// Unknown + int PS4_SYSV_ABI sceNgs2FftInit() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -35,31 +494,6 @@ int PS4_SYSV_ABI sceNgs2FftQuerySize() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2GeomApply() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GeomCalcListener() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GeomResetListenerParam() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GeomResetSourceParam() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2GetWaveformFrameInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2JobSchedulerResetOption() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -80,71 +514,6 @@ int PS4_SYSV_ABI sceNgs2ModuleQueueEnumItems() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2PanGetVolumeMatrix() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2PanInit() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ParseWaveformData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ParseWaveformFile() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ParseWaveformUser() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackCreate() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackCreateWithAllocator() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackDestroy() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackGetInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackGetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackGetVoiceHandle() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackLock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackQueryBufferSize() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2RackQueryInfo() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -155,116 +524,21 @@ int PS4_SYSV_ABI sceNgs2RackRunCommands() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2RackSetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2RackUnlock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ReportRegisterHandler() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2ReportUnregisterHandler() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemCreate() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemCreateWithAllocator() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemDestroy() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemEnumHandles() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemEnumRackHandles() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemGetInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemGetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemLock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemQueryBufferSize() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2SystemQueryInfo() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2SystemRender() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemResetOption() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2SystemRunCommands() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2SystemSetGrainSamples() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2SystemSetLoudThreshold() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2SystemSetSampleRate() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemSetUserData() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2SystemUnlock() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2StreamCreate() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; @@ -300,36 +574,6 @@ int PS4_SYSV_ABI sceNgs2StreamRunCommands() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNgs2VoiceControl() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetMatrixInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetOwner() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetPortInfo() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetState() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - -int PS4_SYSV_ABI sceNgs2VoiceGetStateFlags() { - LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); - return ORBIS_OK; -} - int PS4_SYSV_ABI sceNgs2VoiceQueryInfo() { LOG_ERROR(Lib_Ngs2, "(STUBBED) called"); return ORBIS_OK; diff --git a/src/core/libraries/ngs2/ngs2.h b/src/core/libraries/ngs2/ngs2.h index a5f1f52a6..a34bf21d4 100644 --- a/src/core/libraries/ngs2/ngs2.h +++ b/src/core/libraries/ngs2/ngs2.h @@ -3,7 +3,11 @@ #pragma once +#include "core/libraries/ngs2/ngs2_impl.h" + #include +#include +#include #include "common/types.h" namespace Core::Loader { @@ -12,60 +16,253 @@ class SymbolsResolver; namespace Libraries::Ngs2 { -class Ngs2; +typedef s32 (*OrbisNgs2ParseReadHandler)(uintptr_t userData, u32 offset, void* data, size_t size); -using SceNgs2Handle = Ngs2*; - -enum class SceNgs2HandleType : u32 { - System = 0, +enum class OrbisNgs2HandleType : u32 { + Invalid = 0, + System = 1, + Rack = 2, + Voice = 3, + VoiceControl = 6 }; -struct Ngs2Handle { - void* selfPointer; - void* dataPointer; - std::atomic* atomicPtr; - u32 handleType; - u32 flags_unk; +static const int ORBIS_NGS2_MAX_VOICE_CHANNELS = 8; +static const int ORBIS_NGS2_WAVEFORM_INFO_MAX_BLOCKS = 4; +static const int ORBIS_NGS2_MAX_MATRIX_LEVELS = + (ORBIS_NGS2_MAX_VOICE_CHANNELS * ORBIS_NGS2_MAX_VOICE_CHANNELS); - u32 uid; - u16 maxGrainSamples; - u16 minGrainSamples; - u16 currentGrainSamples; - u16 numGrainSamples; - u16 unknown2; +struct OrbisNgs2WaveformFormat { + u32 waveformType; + u32 numChannels; u32 sampleRate; - u32 unknown3; - - void* flushMutex; - u32 flushMutexInitialized; - void* processMutex; - u32 processMutexInitialized; - - // Linked list pointers for system list - Ngs2Handle* prev; - Ngs2Handle* next; + u32 configData; + u32 frameOffset; + u32 frameMargin; }; -struct SystemOptions { - char padding[6]; - s32 maxGrainSamples; - s32 numGrainSamples; - s32 sampleRate; +struct OrbisNgs2WaveformBlock { + u32 dataOffset; + u32 dataSize; + u32 numRepeats; + u32 numSkipSamples; + u32 numSamples; + u32 reserved; + uintptr_t userData; }; -struct SystemState { - // TODO +struct OrbisNgs2WaveformInfo { + OrbisNgs2WaveformFormat format; + + u32 dataOffset; + u32 dataSize; + + u32 loopBeginPosition; + u32 loopEndPosition; + u32 numSamples; + + u32 audioUnitSize; + u32 numAudioUnitSamples; + u32 numAudioUnitPerFrame; + + u32 audioFrameSize; + u32 numAudioFrameSamples; + + u32 numDelaySamples; + + u32 numBlocks; + OrbisNgs2WaveformBlock aBlock[ORBIS_NGS2_WAVEFORM_INFO_MAX_BLOCKS]; }; -struct StackBuffer { - void** top; - void* base; - void* curr; - size_t usedSize; - size_t totalSize; - size_t alignment; - char isVerifyEnabled; - char padding[7]; +struct OrbisNgs2EnvelopePoint { + u32 curve; + u32 duration; + float height; +}; + +struct OrbisNgs2UserFxProcessContext { + float** aChannelData; + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; + u32 flags; + u32 numChannels; + u32 numGrainSamples; + u32 sampleRate; +}; + +typedef s32 (*OrbisNgs2UserFxProcessHandler)(OrbisNgs2UserFxProcessContext* context); + +struct OrbisNgs2UserFx2SetupContext { + void* common; + void* param; + void* work; + uintptr_t userData; + u32 maxVoices; + u32 voiceIndex; + u64 reserved[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2SetupHandler)(OrbisNgs2UserFx2SetupContext* context); + +struct OrbisNgs2UserFx2CleanupContext { + void* common; + void* param; + void* work; + uintptr_t userData; + u32 maxVoices; + u32 voiceIndex; + u64 reserved[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2CleanupHandler)(OrbisNgs2UserFx2CleanupContext* context); + +struct OrbisNgs2UserFx2ControlContext { + const void* data; + size_t dataSize; + void* common; + void* param; + uintptr_t userData; + u64 reserved[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2ControlHandler)(OrbisNgs2UserFx2ControlContext* context); + +struct OrbisNgs2UserFx2ProcessContext { + float** aChannelData; + void* common; + const void* param; + void* work; + void* state; + uintptr_t userData; + u32 flags; + u32 numInputChannels; + u32 numOutputChannels; + u32 numGrainSamples; + u32 sampleRate; + u32 reserved; + u64 reserved2[4]; +}; + +typedef s32 (*OrbisNgs2UserFx2ProcessHandler)(OrbisNgs2UserFx2ProcessContext* context); + +struct OrbisNgs2BufferAllocator { + OrbisNgs2BufferAllocHandler allocHandler; + OrbisNgs2BufferFreeHandler freeHandler; + uintptr_t userData; +}; + +struct OrbisNgs2RenderBufferInfo { + void* buffer; + size_t bufferSize; + u32 waveformType; + u32 numChannels; +}; + +struct OrbisNgs2RackOption { + size_t size; + char name[ORBIS_NGS2_RACK_NAME_LENGTH]; + + u32 flags; + u32 maxGrainSamples; + u32 maxVoices; + u32 maxInputDelayBlocks; + u32 maxMatrices; + u32 maxPorts; + u32 aReserved[20]; +}; + +struct OrbisNgs2VoiceParamHeader { + u16 size; + s16 next; + u32 id; +}; + +struct OrbisNgs2VoiceMatrixLevelsParam { + OrbisNgs2VoiceParamHeader header; + + u32 matrixId; + u32 numLevels; + const float* aLevel; +}; + +struct OrbisNgs2VoicePortMatrixParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + s32 matrixId; +}; + +struct OrbisNgs2VoicePortVolumeParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + float level; +}; + +struct OrbisNgs2VoicePortDelayParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + u32 numSamples; +}; + +struct OrbisNgs2VoicePatchParam { + OrbisNgs2VoiceParamHeader header; + + u32 port; + u32 destInputId; + OrbisNgs2Handle destHandle; +}; + +struct OrbisNgs2VoiceEventParam { + OrbisNgs2VoiceParamHeader header; + + u32 eventId; +}; + +struct OrbisNgs2VoiceCallbackInfo { + uintptr_t callbackData; + OrbisNgs2Handle voiceHandle; + u32 flag; + u32 reserved; + union { + struct { + uintptr_t userData; + const void* data; + u32 dataSize; + u32 repeatedCount; + u32 attributeFlags; + u32 reserved2; + } waveformBlock; + } param; +}; + +typedef void (*OrbisNgs2VoiceCallbackHandler)(const OrbisNgs2VoiceCallbackInfo* info); + +struct OrbisNgs2VoiceCallbackParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2VoiceCallbackHandler callbackHandler; + + uintptr_t callbackData; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2VoicePortInfo { + s32 matrixId; + float volume; + u32 numDelaySamples; + u32 destInputId; + OrbisNgs2Handle destHandle; +}; + +struct OrbisNgs2VoiceMatrixInfo { + u32 numLevels; + float aLevel[ORBIS_NGS2_MAX_MATRIX_LEVELS]; +}; + +struct OrbisNgs2VoiceState { + u32 stateFlags; }; void RegisterlibSceNgs2(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/ngs2/ngs2_custom.cpp b/src/core/libraries/ngs2/ngs2_custom.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_custom.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_custom.h b/src/core/libraries/ngs2/ngs2_custom.h new file mode 100644 index 000000000..0c45a5d81 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_custom.h @@ -0,0 +1,444 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" +#include "ngs2_reverb.h" + +namespace Libraries::Ngs2 { + +class Ngs2Custom; + +static const int ORBIS_NGS2_CUSTOM_MAX_MODULES = 24; +static const int ORBIS_NGS2_CUSTOM_MAX_PORTS = 16; +static const int ORBIS_NGS2_CUSTOM_DELAY_MAX_TAPS = 8; + +struct OrbisNgs2CustomModuleOption { + u32 size; +}; + +struct OrbisNgs2CustomEnvelopeModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 maxPoints; + u32 reserved; +}; + +struct OrbisNgs2CustomReverbModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 reverbSize; + u32 reserved; +}; + +struct OrbisNgs2CustomChorusModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 maxPhases; + u32 reserved; +} OrbisNgs2CustomChorusModuleOption; + +struct OrbisNgs2CustomPeakMeterModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + u32 numBlocks; + u32 reserved; +}; + +struct OrbisNgs2CustomDelayModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 type; + u32 maxTaps; + float maxLength; + u32 reserved; +}; + +struct OrbisNgs2CustomPitchShiftModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + u32 quality; +}; + +struct OrbisNgs2CustomUserFx2ModuleOption { + OrbisNgs2CustomModuleOption customModuleOption; + + OrbisNgs2UserFx2SetupHandler setupHandler; + OrbisNgs2UserFx2CleanupHandler cleanupHandler; + OrbisNgs2UserFx2ControlHandler controlHandler; + OrbisNgs2UserFx2ProcessHandler processHandler; + + size_t commonSize; + size_t paramSize; + size_t workSize; + uintptr_t userData; +}; + +struct OrbisNgs2CustomRackModuleInfo { + const OrbisNgs2CustomModuleOption* option; + + u32 moduleId; + u32 sourceBufferId; + u32 extraBufferId; + u32 destBufferId; + u32 stateOffset; + u32 stateSize; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomRackPortInfo { + u32 sourceBufferId; + u32 reserved; +}; + +struct OrbisNgs2CustomRackOption { + OrbisNgs2RackOption rackOption; + u32 stateSize; + u32 numBuffers; + u32 numModules; + u32 reserved; + OrbisNgs2CustomRackModuleInfo aModule[ORBIS_NGS2_CUSTOM_MAX_MODULES]; + OrbisNgs2CustomRackPortInfo aPort[ORBIS_NGS2_CUSTOM_MAX_PORTS]; +}; + +struct OrbisNgs2CustomSamplerRackOption { + OrbisNgs2CustomRackOption customRackOption; + + u32 maxChannelWorks; + u32 maxWaveformBlocks; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; + u32 maxCodecCaches; +}; + +struct OrbisNgs2CustomSubmixerRackOption { + OrbisNgs2CustomRackOption customRackOption; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomMasteringRackOption { + OrbisNgs2CustomRackOption customRackOption; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomSamplerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2WaveformFormat format; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2CustomSamplerVoiceWaveformBlocksParam { + OrbisNgs2VoiceParamHeader header; + const void* data; + u32 flags; + u32 numBlocks; + const OrbisNgs2WaveformBlock* aBlock; +}; + +struct OrbisNgs2CustomSamplerVoiceWaveformAddressParam { + OrbisNgs2VoiceParamHeader header; + const void* from; + const void* to; +}; + +struct OrbisNgs2CustomSamplerVoiceWaveformFrameOffsetParam { + OrbisNgs2VoiceParamHeader header; + u32 frameOffset; + u32 reserved; +}; + +struct OrbisNgs2CustomSamplerVoiceExitLoopParam { + OrbisNgs2VoiceParamHeader header; +}; + +struct OrbisNgs2CustomSamplerVoicePitchParam { + OrbisNgs2VoiceParamHeader header; + float ratio; + u32 reserved; +}; + +struct OrbisNgs2CustomSamplerVoiceState { + OrbisNgs2VoiceState voiceState; + char padding[32]; + const void* waveformData; + u64 numDecodedSamples; + u64 decodedDataSize; + u64 userData; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomSubmixerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + u32 numInputChannels; + u32 numOutputChannels; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2CustomSubmixerVoiceState { + OrbisNgs2VoiceState voiceState; // Voice state + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomMasteringVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + u32 numInputChannels; + u32 flags; +}; + +struct OrbisNgs2CustomMasteringVoiceOutputParam { + OrbisNgs2VoiceParamHeader header; + u32 outputId; + u32 reserved; +}; + +struct OrbisNgs2CustomMasteringVoiceState { + OrbisNgs2VoiceState voiceState; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomVoiceEnvelopeParam { + OrbisNgs2VoiceParamHeader header; + u32 numForwardPoints; + u32 numReleasePoints; + const OrbisNgs2EnvelopePoint* aPoint; +}; + +struct OrbisNgs2CustomVoiceDistortionParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + float a; + float b; + float clip; + float gate; + float wetLevel; + float dryLevel; + u32 reserved; +}; + +struct OrbisNgs2CustomVoiceCompressorParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + float threshold; + float ratio; + float knee; + float attackTime; + float releaseTime; + float level; + u32 reserved; +}; + +struct OrbisNgs2CustomVoiceFilterParam { + OrbisNgs2VoiceParamHeader header; + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; + u32 reserved3; +}; + +struct OrbisNgs2CustomVoiceLfeFilterParam { + OrbisNgs2VoiceParamHeader header; + u32 enableFlag; + u32 fc; +}; + +struct OrbisNgs2CustomVoiceGainParam { + OrbisNgs2VoiceParamHeader header; + float aLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2CustomVoiceMixerParam { + OrbisNgs2VoiceParamHeader header; + float aSourceLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + float aDestLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2CustomVoiceChannelMixerParam { + OrbisNgs2VoiceParamHeader header; + float aLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS][ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2CustomVoiceUserFxParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2UserFxProcessHandler handler; + + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; +}; + +struct OrbisNgs2CustomVoiceUserFx2Param { + OrbisNgs2VoiceParamHeader header; + const void* data; + size_t dataSize; +}; + +struct OrbisNgs2CustomVoiceOutputParam { + OrbisNgs2VoiceParamHeader header; + u32 outputId; + u32 reserved; +}; + +struct OrbisNgs2CustomVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + u32 enableFlag; + u32 reserved; +} OrbisNgs2CustomVoicePeakMeterParam; + +struct OrbisNgs2CustomVoiceReverbParam { + OrbisNgs2VoiceParamHeader header; + OrbisNgs2ReverbI3DL2Param i3dl2; +}; + +struct OrbisNgs2CustomVoiceChorusParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + u32 numPhases; + u32 channelMask; + float inputLevel; + float delayTime; + float modulationRatio; + float modulationDepth; + float feedbackLevel; + float wetLevel; + float dryLevel; +}; + +struct OrbisNgs2DelayTapInfo { + float tapLevel; + float delayTime; +}; + +struct OrbisNgs2CustomVoiceDelayParam { + OrbisNgs2VoiceParamHeader header; + float dryLevel; + float wetLevel; + float inputLevel; + float feedbackLevel; + float lowpassFc; + u32 numTaps; + OrbisNgs2DelayTapInfo aTap[ORBIS_NGS2_CUSTOM_DELAY_MAX_TAPS]; + float aInputMixLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + u32 channelMask; + u32 flags; +}; + +struct OrbisNgs2CustomVoiceNoiseGateParam { + OrbisNgs2VoiceParamHeader header; + u32 flags; + float threshold; + float attackTime; + float releaseTime; +}; + +struct OrbisNgs2CustomVoicePitchShiftParam { + OrbisNgs2VoiceParamHeader header; + s32 cent; +}; + +struct OrbisNgs2CustomEnvelopeModuleState { + float height; + u32 reserved; +}; + +struct OrbisNgs2CustomCompressorModuleState { + float peakHeight; + float compressorHeight; +}; + +struct OrbisNgs2CustomPeakMeterModuleState { + float peak; + float aChannelPeak[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + u32 reserved; +}; + +struct OrbisNgs2CustomNoiseGateModuleState { + float gateHeight; +}; + +struct OrbisNgs2CustomRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 stateSize; + u32 numBuffers; + u32 numModules; + u32 reserved; + OrbisNgs2CustomRackModuleInfo aModule[ORBIS_NGS2_CUSTOM_MAX_MODULES]; + OrbisNgs2CustomRackPortInfo aPort[ORBIS_NGS2_CUSTOM_MAX_PORTS]; +}; + +struct OrbisNgs2CustomSamplerRackInfo { + OrbisNgs2CustomRackInfo customRackInfo; + + u32 maxChannelWorks; + u32 maxWaveformBlocks; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; + u32 maxCodecCaches; +}; + +struct OrbisNgs2CustomSubmixerRackInfo { + OrbisNgs2CustomRackInfo customRackInfo; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomMasteringRackInfo { + OrbisNgs2CustomRackInfo customRackInfo; + + u32 maxChannels; + u32 maxInputs; +}; + +struct OrbisNgs2CustomModuleInfo { + u32 moduleId; + u32 sourceBufferId; + u32 extraBufferId; + u32 destBufferId; + u32 stateOffset; + u32 stateSize; + u32 reserved; + u32 reserved2; +}; + +struct OrbisNgs2CustomEnvelopeModuleInfo { + OrbisNgs2CustomModuleInfo moduleInfo; + + u32 maxPoints; + u32 reserved; +}; + +struct OrbisNgs2CustomReverbModuleInfo { + OrbisNgs2CustomModuleInfo moduleInfo; + + u32 reverbSize; + u32 reserved; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_eq.cpp b/src/core/libraries/ngs2/ngs2_eq.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_eq.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_eq.h b/src/core/libraries/ngs2/ngs2_eq.h new file mode 100644 index 000000000..99688f24e --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_eq.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Eq; + +struct OrbisNgs2EqVoiceSetupParam { + u32 numChannels; +}; + +struct OrbisNgs2EqVoiceFilterParam { + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; +}; + +struct OrbisNgs2EqVoiceState { + u32 stateFlags; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_geom.cpp b/src/core/libraries/ngs2/ngs2_geom.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_geom.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_geom.h b/src/core/libraries/ngs2/ngs2_geom.h new file mode 100644 index 000000000..93af99d8d --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_geom.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Geom; + +struct OrbisNgs2GeomVector { + float x; + float y; + float z; +}; + +struct OrbisNgs2GeomCone { + float innerLevel; + float innerAngle; + float outerLevel; + float outerAngle; +}; + +struct OrbisNgs2GeomRolloff { + u32 model; + float maxDistance; + float rolloffFactor; + float referenceDistance; +}; + +struct OrbisNgs2GeomListenerParam { + OrbisNgs2GeomVector position; + OrbisNgs2GeomVector orientFront; + OrbisNgs2GeomVector orientUp; + OrbisNgs2GeomVector velocity; + float soundSpeed; + u32 reserved[2]; +}; + +struct OrbisNgs2GeomListenerWork { + float matrix[4][4]; + OrbisNgs2GeomVector velocity; + float soundSpeed; + u32 coordinate; + u32 reserved[3]; +}; + +struct OrbisNgs2GeomSourceParam { + OrbisNgs2GeomVector position; + OrbisNgs2GeomVector velocity; + OrbisNgs2GeomVector direction; + OrbisNgs2GeomCone cone; + OrbisNgs2GeomRolloff rolloff; + float dopplerFactor; + float fbwLevel; + float lfeLevel; + float maxLevel; + float minLevel; + float radius; + u32 numSpeakers; + u32 matrixFormat; + u32 reserved[2]; +}; + +struct OrbisNgs2GeomA3dAttribute { + OrbisNgs2GeomVector position; + float volume; + u32 reserved[4]; +}; + +struct OrbisNgs2GeomAttribute { + float pitchRatio; + float aLevel[ORBIS_NGS2_MAX_VOICE_CHANNELS * ORBIS_NGS2_MAX_VOICE_CHANNELS]; + + OrbisNgs2GeomA3dAttribute a3dAttrib; + u32 reserved[4]; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_impl.cpp b/src/core/libraries/ngs2/ngs2_impl.cpp index b358a05f7..1248f76d7 100644 --- a/src/core/libraries/ngs2/ngs2_impl.cpp +++ b/src/core/libraries/ngs2/ngs2_impl.cpp @@ -12,153 +12,171 @@ using namespace Libraries::Kernel; namespace Libraries::Ngs2 { -s32 Ngs2::ReportInvalid(Ngs2Handle* handle, u32 handle_type) const { - uintptr_t hAddress = reinterpret_cast(handle); - switch (handle_type) { +s32 HandleReportInvalid(OrbisNgs2Handle handle, u32 handleType) { + switch (handleType) { case 1: - LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE; case 2: - LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE; case 4: - LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE; case 8: - LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE; default: - LOG_ERROR(Lib_Ngs2, "Invalid handle {}", hAddress); + LOG_ERROR(Lib_Ngs2, "Invalid handle {}", handle); return ORBIS_NGS2_ERROR_INVALID_HANDLE; } } -s32 Ngs2::HandleSetup(Ngs2Handle* handle, void* data, std::atomic* atomic, u32 type, - u32 flags) { - handle->dataPointer = data; - handle->atomicPtr = atomic; - handle->handleType = type; - handle->flags_unk = flags; - return ORBIS_OK; +void* MemoryClear(void* buffer, size_t size) { + return memset(buffer, 0, size); } -s32 Ngs2::HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut) { - if (handle && handle->selfPointer == handle) { - std::atomic* tmp_atomic = handle->atomicPtr; - if (tmp_atomic && handle->handleType == hType) { - while (tmp_atomic->load() != 0) { - u32 expected = 1; - if (tmp_atomic->compare_exchange_strong(expected, 0)) { - if (dataOut) { - dataOut = handle->dataPointer; - } - // sceNgs2MemoryClear(handle, 32); - return ORBIS_OK; - } - tmp_atomic = handle->atomicPtr; - } - } - } - return this->ReportInvalid(handle, hType); -} - -s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) { - if (!handle) { - return this->ReportInvalid(handle, 0); - } - - if (handle->selfPointer != handle || !handle->atomicPtr || !handle->dataPointer || - (~hType & handle->handleType)) { - return this->ReportInvalid(handle, handle->handleType); - } - - std::atomic* atomic = handle->atomicPtr; - while (true) { - u32 i = atomic->load(); - if (i == 0) { - return this->ReportInvalid(handle, handle->handleType); - } - if (atomic->compare_exchange_strong(i, i + 1)) { - break; - } - } - - if (handleOut) { - handleOut = handle; +s32 StackBufferClose(StackBuffer* stackBuffer, size_t* outTotalSize) { + if (outTotalSize) { + *outTotalSize = stackBuffer->usedSize + stackBuffer->alignment; } return ORBIS_OK; } -s32 Ngs2::HandleLeave(Ngs2Handle* handle) { - std::atomic* tmp_atomic; - u32 i; - do { - tmp_atomic = handle->atomicPtr; - i = tmp_atomic->load(); - } while (!tmp_atomic->compare_exchange_strong(i, i - 1)); - return ORBIS_OK; -} +s32 StackBufferOpen(StackBuffer* stackBuffer, void* bufferStart, size_t bufferSize, + void** outBuffer, u8 flags) { + stackBuffer->top = outBuffer; + stackBuffer->base = bufferStart; + stackBuffer->size = (size_t)bufferStart; + stackBuffer->currentOffset = (size_t)bufferStart; + stackBuffer->usedSize = 0; + stackBuffer->totalSize = bufferSize; + stackBuffer->alignment = 8; // this is a fixed value + stackBuffer->flags = flags; -s32 Ngs2::StackBufferOpen(StackBuffer* buf, void* base_addr, size_t size, void** stackTop, - bool verify) { - buf->top = stackTop; - buf->base = base_addr; - buf->curr = base_addr; - buf->usedSize = 0; - buf->totalSize = size; - buf->alignment = 8; - buf->isVerifyEnabled = verify; - - if (stackTop) { - *stackTop = nullptr; + if (outBuffer != NULL) { + *outBuffer = NULL; } return ORBIS_OK; } -s32 Ngs2::StackBufferClose(StackBuffer* buf, size_t* usedSize) { - if (usedSize) { - *usedSize = buf->usedSize + buf->alignment; +s32 SystemCleanup(OrbisNgs2Handle systemHandle, OrbisNgs2ContextBufferInfo* outInfo) { + if (!systemHandle) { + return ORBIS_NGS2_ERROR_INVALID_HANDLE; } + // TODO + return ORBIS_OK; } -s32 Ngs2::SystemSetupCore(StackBuffer* buf, SystemOptions* options, Ngs2Handle** sysOut) { +s32 SystemSetupCore(StackBuffer* stackBuffer, const OrbisNgs2SystemOption* option, + SystemInternal* outSystem) { u32 maxGrainSamples = 512; u32 numGrainSamples = 256; u32 sampleRate = 48000; - if (options) { - maxGrainSamples = options->maxGrainSamples; - numGrainSamples = options->numGrainSamples; - sampleRate = options->sampleRate; + if (option) { + sampleRate = option->sampleRate; + maxGrainSamples = option->maxGrainSamples; + numGrainSamples = option->numGrainSamples; } - // Validate maxGrainSamples - if (maxGrainSamples < 64 || maxGrainSamples > 1024 || (maxGrainSamples & 0x3F) != 0) { + if (maxGrainSamples < 64 || maxGrainSamples > 1024 || (maxGrainSamples & 63) != 0) { LOG_ERROR(Lib_Ngs2, "Invalid system option (maxGrainSamples={},x64)", maxGrainSamples); return ORBIS_NGS2_ERROR_INVALID_MAX_GRAIN_SAMPLES; } - // Validate numGrainSamples - if (numGrainSamples < 64 || numGrainSamples > 1024 || (numGrainSamples & 0x3F) != 0) { + if (numGrainSamples < 64 || numGrainSamples > 1024 || (numGrainSamples & 63) != 0) { LOG_ERROR(Lib_Ngs2, "Invalid system option (numGrainSamples={},x64)", numGrainSamples); return ORBIS_NGS2_ERROR_INVALID_NUM_GRAIN_SAMPLES; } - // Validate sampleRate if (sampleRate != 11025 && sampleRate != 12000 && sampleRate != 22050 && sampleRate != 24000 && - sampleRate != 44100 && sampleRate != 48000 && sampleRate != 88200 && sampleRate != 96000) { + sampleRate != 44100 && sampleRate != 48000 && sampleRate != 88200 && sampleRate != 96000 && + sampleRate != 176400 && sampleRate != 192000) { LOG_ERROR(Lib_Ngs2, "Invalid system option(sampleRate={}:44.1/48kHz series)", sampleRate); return ORBIS_NGS2_ERROR_INVALID_SAMPLE_RATE; } - int result = ORBIS_OK; + return ORBIS_OK; +} +s32 SystemSetup(const OrbisNgs2SystemOption* option, OrbisNgs2ContextBufferInfo* hostBufferInfo, + OrbisNgs2BufferFreeHandler hostFree, OrbisNgs2Handle* outHandle) { + u8 optionFlags = 0; + StackBuffer stackBuffer; + SystemInternal setupResult; + void* systemList = NULL; + size_t requiredBufferSize = 0; + u32 result = ORBIS_NGS2_ERROR_INVALID_BUFFER_SIZE; + + if (option) { + if (option->size != 64) { + LOG_ERROR(Lib_Ngs2, "Invalid system option size ({})", option->size); + return ORBIS_NGS2_ERROR_INVALID_OPTION_SIZE; + } + optionFlags = option->flags >> 31; + } + + // Init + StackBufferOpen(&stackBuffer, NULL, 0, NULL, optionFlags); + result = SystemSetupCore(&stackBuffer, option, 0); + + if (result < 0) { + return result; + } + + StackBufferClose(&stackBuffer, &requiredBufferSize); + + // outHandle unprovided + if (!outHandle) { + hostBufferInfo->hostBuffer = NULL; + hostBufferInfo->hostBufferSize = requiredBufferSize; + MemoryClear(&hostBufferInfo->reserved, sizeof(hostBufferInfo->reserved)); + return ORBIS_OK; + } + + if (!hostBufferInfo->hostBuffer) { + LOG_ERROR(Lib_Ngs2, "Invalid system buffer address ({})", hostBufferInfo->hostBuffer); + return ORBIS_NGS2_ERROR_INVALID_BUFFER_ADDRESS; + } + + if (hostBufferInfo->hostBufferSize < requiredBufferSize) { + LOG_ERROR(Lib_Ngs2, "Invalid system buffer size ({}<{}[byte])", + hostBufferInfo->hostBufferSize, requiredBufferSize); + return ORBIS_NGS2_ERROR_INVALID_BUFFER_SIZE; + } + + // Setup + StackBufferOpen(&stackBuffer, hostBufferInfo->hostBuffer, hostBufferInfo->hostBufferSize, + &systemList, optionFlags); + result = SystemSetupCore(&stackBuffer, option, &setupResult); + + if (result < 0) { + return result; + } + + StackBufferClose(&stackBuffer, &requiredBufferSize); + + // Copy buffer results + setupResult.bufferInfo = *hostBufferInfo; + setupResult.hostFree = hostFree; // TODO + // setupResult.systemList = systemList; - return result; // Success + OrbisNgs2Handle systemHandle = setupResult.systemHandle; + if (hostBufferInfo->hostBufferSize >= requiredBufferSize) { + *outHandle = systemHandle; + return ORBIS_OK; + } + + SystemCleanup(systemHandle, 0); + + LOG_ERROR(Lib_Ngs2, "Invalid system buffer size ({}<{}[byte])", hostBufferInfo->hostBufferSize, + requiredBufferSize); + return ORBIS_NGS2_ERROR_INVALID_BUFFER_SIZE; } } // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_impl.h b/src/core/libraries/ngs2/ngs2_impl.h index fea87c51c..7be0f89cc 100644 --- a/src/core/libraries/ngs2/ngs2_impl.h +++ b/src/core/libraries/ngs2/ngs2_impl.h @@ -3,23 +3,176 @@ #pragma once -#include "ngs2.h" +#include "core/libraries/kernel/threads/pthread.h" namespace Libraries::Ngs2 { -class Ngs2 { -public: - s32 ReportInvalid(Ngs2Handle* handle, u32 handle_type) const; - s32 HandleSetup(Ngs2Handle* handle, void* data, std::atomic* atomic, u32 type, u32 flags); - s32 HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut); - s32 HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut); - s32 HandleLeave(Ngs2Handle* handle); - s32 StackBufferOpen(StackBuffer* buf, void* base_addr, size_t size, void** stackTop, - bool verify); - s32 StackBufferClose(StackBuffer* buf, size_t* usedSize); - s32 SystemSetupCore(StackBuffer* buf, SystemOptions* options, Ngs2Handle** sysOut); +static const int ORBIS_NGS2_SYSTEM_NAME_LENGTH = 16; +static const int ORBIS_NGS2_RACK_NAME_LENGTH = 16; -private: +typedef uintptr_t OrbisNgs2Handle; + +struct OrbisNgs2ContextBufferInfo { + void* hostBuffer; + size_t hostBufferSize; + uintptr_t reserved[5]; + uintptr_t userData; }; +struct OrbisNgs2SystemOption { + size_t size; + char name[ORBIS_NGS2_SYSTEM_NAME_LENGTH]; + + u32 flags; + u32 maxGrainSamples; + u32 numGrainSamples; + u32 sampleRate; + u32 aReserved[6]; +}; + +typedef s32 (*OrbisNgs2BufferAllocHandler)(OrbisNgs2ContextBufferInfo* ioBufferInfo); +typedef s32 (*OrbisNgs2BufferFreeHandler)(OrbisNgs2ContextBufferInfo* ioBufferInfo); + +struct OrbisNgs2SystemInfo { + char name[ORBIS_NGS2_SYSTEM_NAME_LENGTH]; // 0 + + OrbisNgs2Handle systemHandle; // 16 + OrbisNgs2ContextBufferInfo bufferInfo; // 24 + + u32 uid; // 88 + u32 minGrainSamples; // 92 + u32 maxGrainSamples; // 96 + + u32 stateFlags; // 100 + u32 rackCount; // 104 + float lastRenderRatio; // 108 + s64 lastRenderTick; // 112 + s64 renderCount; // 120 + u32 sampleRate; // 128 + u32 numGrainSamples; // 132 +}; + +struct OrbisNgs2RackInfo { + char name[ORBIS_NGS2_RACK_NAME_LENGTH]; // 0 + + OrbisNgs2Handle rackHandle; // 16 + OrbisNgs2ContextBufferInfo bufferInfo; // 24 + + OrbisNgs2Handle ownerSystemHandle; // 88 + + u32 type; // 96 + u32 rackId; // 100 + u32 uid; // 104 + u32 minGrainSamples; // 108 + u32 maxGrainSamples; // 112 + u32 maxVoices; // 116 + u32 maxChannelWorks; // 120 + u32 maxInputs; // 124 + u32 maxMatrices; // 128 + u32 maxPorts; // 132 + + u32 stateFlags; // 136 + float lastProcessRatio; // 140 + u64 lastProcessTick; // 144 + u64 renderCount; // 152 + u32 activeVoiceCount; // 160 + u32 activeChannelWorkCount; // 164 +}; + +struct StackBuffer { + void** top; + void* base; + size_t size; + size_t currentOffset; + size_t usedSize; + size_t totalSize; + size_t alignment; + u8 flags; + char padding[7]; +}; + +struct SystemInternal { + // setup init + char name[ORBIS_NGS2_SYSTEM_NAME_LENGTH]; // 0 + OrbisNgs2ContextBufferInfo bufferInfo; // 16 + OrbisNgs2BufferFreeHandler hostFree; // 80 + OrbisNgs2Handle systemHandle; // 88 + void* unknown1; // 96 + void* unknown2; // 104 + OrbisNgs2Handle rackHandle; // 112 + uintptr_t* userData; // 120 + SystemInternal* systemList; // 128 + StackBuffer* stackBuffer; // 136 + OrbisNgs2SystemInfo ownerSystemInfo; // 144 + + struct rackList { + void* prev; + void* next; + void* unknown; + }; + + rackList rackListPreset; // 152 + rackList rackListNormal; // 176 + rackList rackListMaster; // 200 + + void* unknown3; // 208 + void* systemListPrev; // 216 + void* unknown4; // 224 + void* systemListNext; // 232 + void* rackFunction; // 240 + + Kernel::PthreadMutex processLock; // 248 + u32 hasProcessMutex; // 256 + u32 unknown5; // 260 + Kernel::PthreadMutex flushLock; // 264 + u32 hasFlushMutex; // 272 + u32 unknown6; // 276 + + // info + u64 lastRenderTick; // 280 + u64 renderCount; // 288 + u32 isActive; // 296 + std::atomic lockCount; // 300 + u32 uid; // 304 + u32 systemType; // 308 + + struct { + u8 isBufferValid : 1; + u8 isRendering : 1; + u8 isSorted : 1; + u8 isFlushReady : 1; + } flags; // 312 + + u16 currentMaxGrainSamples; // 316 + u16 minGrainSamples; // 318 + u16 maxGrainSamples; // 320 + u16 numGrainSamples; // 322 + u32 currentNumGrainSamples; // 324 + u32 sampleRate; // 328 + u32 currentSampleRate; // 332 + u32 rackCount; // 336 + float lastRenderRatio; // 340 + float cpuLoad; // 344 +}; + +struct HandleInternal { + HandleInternal* selfPtr; // 0 + SystemInternal* systemData; // 8 + std::atomic refCount; // 16 + u32 handleType; // 24 + u32 handleID; // 28 +}; + +s32 StackBufferClose(StackBuffer* stackBuffer, size_t* outTotalSize); +s32 StackBufferOpen(StackBuffer* stackBuffer, void* buffer, size_t bufferSize, void** outBuffer, + u8 flags); +s32 SystemSetupCore(StackBuffer* stackBuffer, const OrbisNgs2SystemOption* option, + SystemInternal* outSystem); + +s32 HandleReportInvalid(OrbisNgs2Handle handle, u32 handleType); +void* MemoryClear(void* buffer, size_t size); +s32 SystemCleanup(OrbisNgs2Handle systemHandle, OrbisNgs2ContextBufferInfo* outInfo); +s32 SystemSetup(const OrbisNgs2SystemOption* option, OrbisNgs2ContextBufferInfo* hostBufferInfo, + OrbisNgs2BufferFreeHandler hostFree, OrbisNgs2Handle* outHandle); + } // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_mastering.cpp b/src/core/libraries/ngs2/ngs2_mastering.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_mastering.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_mastering.h b/src/core/libraries/ngs2/ngs2_mastering.h new file mode 100644 index 000000000..e0ba478c3 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_mastering.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Mastering; + +struct OrbisNgs2MasteringRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannels; + u32 numPeakMeterBlocks; +}; + +struct OrbisNgs2MasteringVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + + u32 numInputChannels; + u32 flags; +}; + +struct OrbisNgs2MasteringVoiceMatrixParam { + OrbisNgs2VoiceParamHeader header; + + u32 type; + u32 numLevels; + const float* aLevel; +}; + +struct OrbisNgs2MasteringVoiceLfeParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + u32 fc; +}; + +struct OrbisNgs2MasteringVoiceLimiterParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + float threshold; +}; + +struct OrbisNgs2MasteringVoiceGainParam { + OrbisNgs2VoiceParamHeader header; + + float fbwLevel; + float lfeLevel; +}; + +struct OrbisNgs2MasteringVoiceOutputParam { + OrbisNgs2VoiceParamHeader header; + + u32 outputId; + u32 reserved; +}; + +struct OrbisNgs2MasteringVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + u32 enableFlag; + u32 reserved; +}; + +struct OrbisNgs2MasteringVoiceState { + OrbisNgs2VoiceState voiceState; + float limiterPeakLevel; + float limiterPressLevel; + float aInputPeakHeight[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + float aOutputPeakHeight[ORBIS_NGS2_MAX_VOICE_CHANNELS]; +}; + +struct OrbisNgs2MasteringRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannels; + u32 reserved; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_pan.cpp b/src/core/libraries/ngs2/ngs2_pan.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_pan.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_pan.h b/src/core/libraries/ngs2/ngs2_pan.h new file mode 100644 index 000000000..d39ec67cd --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_pan.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Pan; + +struct OrbisNgs2PanParam { + float angle; + float distance; + float fbwLevel; + float lfeLevel; +}; + +struct OrbisNgs2PanWork { + float aSpeakerAngle[ORBIS_NGS2_MAX_VOICE_CHANNELS]; + float unitAngle; + u32 numSpeakers; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_report.cpp b/src/core/libraries/ngs2/ngs2_report.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_report.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_report.h b/src/core/libraries/ngs2/ngs2_report.h new file mode 100644 index 000000000..88f6d1df0 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_report.h @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +#include // va_list + +namespace Libraries::Ngs2 { + +class Ngs2Report; + +struct OrbisNgs2ReportDataHeader { + size_t size; + OrbisNgs2Handle handle; + u32 type; + s32 result; +}; + +typedef void (*OrbisNgs2ReportHandler)(const OrbisNgs2ReportDataHeader* data, uintptr_t userData); + +struct OrbisNgs2ReportMessageData { + OrbisNgs2ReportDataHeader header; + const char* message; +}; + +struct OrbisNgs2ReportApiData { + OrbisNgs2ReportDataHeader header; + const char* functionName; + const char* format; + va_list argument; +}; + +struct OrbisNgs2ReportControlData { + OrbisNgs2ReportDataHeader header; + const OrbisNgs2VoiceParamHeader* param; +}; + +struct OrbisNgs2ReportOutputData { + OrbisNgs2ReportDataHeader header; + const OrbisNgs2RenderBufferInfo* bufferInfo; + + u32 bufferIndex; + u32 sampleRate; + u32 numGrainSamples; + u32 reserved; +}; + +struct OrbisNgs2ReportCpuLoadData { + OrbisNgs2ReportDataHeader header; + float totalRatio; + float flushRatio; + float processRatio; + float feedbackRatio; +}; + +struct OrbisNgs2ReportRenderStateData { + OrbisNgs2ReportDataHeader header; + u32 state; + u32 reserved; +}; + +struct OrbisNgs2ReportVoiceWaveformData { + OrbisNgs2ReportDataHeader header; + u32 location; + u32 waveformType; + u32 numChannels; + u32 sampleRate; + u32 numGrainSamples; + u32 reserved; + void* const* aData; +}; + +s32 PS4_SYSV_ABI sceNgs2ReportRegisterHandler(u32 reportType, OrbisNgs2ReportHandler handler, + uintptr_t userData, OrbisNgs2Handle* outHandle); + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_reverb.cpp b/src/core/libraries/ngs2/ngs2_reverb.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_reverb.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_reverb.h b/src/core/libraries/ngs2/ngs2_reverb.h new file mode 100644 index 000000000..715d7480a --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_reverb.h @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Reverb; + +struct OrbisNgs2ReverbRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannels; + u32 reverbSize; +}; + +struct OrbisNgs2ReverbI3DL2Param { + float wet; + float dry; + s32 room; + s32 roomHF; + u32 reflectionPattern; + float decayTime; + float decayHFRatio; + s32 reflections; + float reflectionsDelay; + s32 reverb; + float reverbDelay; + float diffusion; + float density; + float HFReference; + u32 reserve[8]; +}; + +struct OrbisNgs2ReverbVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + + u32 numInputChannels; + u32 numOutputChannels; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2ReverbVoiceI3DL2Param { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2ReverbI3DL2Param i3dl2; +}; + +struct OrbisNgs2ReverbVoiceState { + OrbisNgs2VoiceState voiceState; +}; + +struct OrbisNgs2ReverbRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannels; + u32 reverbSize; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_sampler.cpp b/src/core/libraries/ngs2/ngs2_sampler.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_sampler.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_sampler.h b/src/core/libraries/ngs2/ngs2_sampler.h new file mode 100644 index 000000000..0842b9cb2 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_sampler.h @@ -0,0 +1,162 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Sampler; + +struct OrbisNgs2SamplerRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannelWorks; + u32 maxCodecCaches; + u32 maxWaveformBlocks; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; + u32 numPeakMeterBlocks; +}; + +struct OrbisNgs2SamplerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2WaveformFormat format; + u32 flags; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceWaveformBlocksParam { + OrbisNgs2VoiceParamHeader header; + + const void* data; + u32 flags; + u32 numBlocks; + const OrbisNgs2WaveformBlock* aBlock; + // Blocks +}; + +struct OrbisNgs2SamplerVoiceWaveformAddressParam { + OrbisNgs2VoiceParamHeader header; + + const void* from; + const void* to; +}; + +struct OrbisNgs2SamplerVoiceWaveformFrameOffsetParam { + OrbisNgs2VoiceParamHeader header; + + u32 frameOffset; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceExitLoopParam { + OrbisNgs2VoiceParamHeader header; +}; + +struct OrbisNgs2SamplerVoicePitchParam { + OrbisNgs2VoiceParamHeader header; + + float ratio; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceEnvelopeParam { + OrbisNgs2VoiceParamHeader header; + + u32 numForwardPoints; + u32 numReleasePoints; + const OrbisNgs2EnvelopePoint* aPoint; +}; + +struct OrbisNgs2SamplerVoiceDistortionParam { + OrbisNgs2VoiceParamHeader header; + + u32 flags; + float a; + float b; + float clip; + float gate; + float wetLevel; + float dryLevel; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceUserFxParam { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2UserFxProcessHandler handler; + + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; +}; + +struct OrbisNgs2SamplerVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceFilterParam { + OrbisNgs2VoiceParamHeader header; + + u32 index; + u32 location; + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; + u32 reserved3; +}; + +struct OrbisNgs2SamplerVoiceNumFilters { + OrbisNgs2VoiceParamHeader header; + + u32 numFilters; + u32 reserved; +}; + +struct OrbisNgs2SamplerVoiceState { + OrbisNgs2VoiceState voiceState; + float envelopeHeight; + float peakHeight; + u32 reserved; + u64 numDecodedSamples; + u64 decodedDataSize; + u64 userData; + const void* waveformData; +}; + +struct OrbisNgs2SamplerRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannelWorks; + u32 maxCodecCaches; + u32 maxWaveformBlocks; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxAtrac9Decoders; + u32 maxAtrac9ChannelWorks; + u32 maxAjmAtrac9Decoders; +}; + +} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_submixer.cpp b/src/core/libraries/ngs2/ngs2_submixer.cpp new file mode 100644 index 000000000..8c82e4e49 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_submixer.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ngs2_error.h" +#include "ngs2_impl.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" + +using namespace Libraries::Kernel; + +namespace Libraries::Ngs2 {} // namespace Libraries::Ngs2 diff --git a/src/core/libraries/ngs2/ngs2_submixer.h b/src/core/libraries/ngs2/ngs2_submixer.h new file mode 100644 index 000000000..df2d8a835 --- /dev/null +++ b/src/core/libraries/ngs2/ngs2_submixer.h @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "ngs2.h" + +namespace Libraries::Ngs2 { + +class Ngs2Submixer; + +struct OrbisNgs2SubmixerRackOption { + OrbisNgs2RackOption rackOption; + u32 maxChannels; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxInputs; + u32 numPeakMeterBlocks; +}; + +struct OrbisNgs2SubmixerVoiceSetupParam { + OrbisNgs2VoiceParamHeader header; + u32 numIoChannels; + u32 flags; +}; + +struct OrbisNgs2SubmixerVoiceEnvelopeParam { + OrbisNgs2VoiceParamHeader header; + + u32 numForwardPoints; + u32 numReleasePoints; + const OrbisNgs2EnvelopePoint* aPoint; +}; + +struct OrbisNgs2SubmixerVoiceCompressorParam { + OrbisNgs2VoiceParamHeader header; + + u32 flags; + float threshold; + float ratio; + float knee; + float attackTime; + float releaseTime; + float level; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceDistortionParam { + OrbisNgs2VoiceParamHeader header; + + u32 flags; + float a; + float b; + float clip; + float gate; + float wetLevel; + float dryLevel; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceUserFxParam { + OrbisNgs2VoiceParamHeader header; + + OrbisNgs2UserFxProcessHandler handler; + + uintptr_t userData0; + uintptr_t userData1; + uintptr_t userData2; +}; + +struct OrbisNgs2SubmixerVoicePeakMeterParam { + OrbisNgs2VoiceParamHeader header; + + u32 enableFlag; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceFilterParam { + OrbisNgs2VoiceParamHeader header; + + u32 index; + u32 location; + u32 type; + u32 channelMask; + union { + struct { + float i0; + float i1; + float i2; + float o1; + float o2; + } direct; + struct { + float fc; + float q; + float level; + u32 reserved; + u32 reserved2; + } fcq; + } param; + u32 reserved3; +}; + +struct OrbisNgs2SubmixerVoiceNumFilters { + OrbisNgs2VoiceParamHeader header; + + u32 numFilters; + u32 reserved; +}; + +struct OrbisNgs2SubmixerVoiceState { + OrbisNgs2VoiceState voiceState; + float envelopeHeight; + float peakHeight; + float compressorHeight; +}; + +struct OrbisNgs2SubmixerRackInfo { + OrbisNgs2RackInfo rackInfo; + u32 maxChannels; + u32 maxEnvelopePoints; + u32 maxFilters; + u32 maxInputs; +}; + +} // namespace Libraries::Ngs2 From 9c37aa039bff93a2dd11f1aeb500cc047bc6d817 Mon Sep 17 00:00:00 2001 From: illusion0001 <37698908+illusion0001@users.noreply.github.com> Date: Thu, 27 Mar 2025 09:50:21 +1300 Subject: [PATCH 27/52] Add isDevKit bool (#2685) --- src/common/config.cpp | 8 ++++++++ src/common/config.h | 1 + src/core/memory.cpp | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/src/common/config.cpp b/src/common/config.cpp index e0a348fbe..16d9e5724 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -32,6 +32,7 @@ std::filesystem::path find_fs_path_or(const basic_value& v, const K& ky, namespace Config { static bool isNeo = false; +static bool isDevKit = false; static bool playBGM = false; static bool isTrophyPopupDisabled = false; static int BGMvolume = 50; @@ -167,6 +168,10 @@ bool isNeoModeConsole() { return isNeo; } +bool isDevKitConsole() { + return isDevKit; +} + bool getIsFullscreen() { return isFullscreen; } @@ -755,6 +760,7 @@ void load(const std::filesystem::path& path) { const toml::value& general = data.at("General"); isNeo = toml::find_or(general, "isPS4Pro", false); + isDevKit = toml::find_or(general, "isDevKit", false); playBGM = toml::find_or(general, "playBGM", false); isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false); trophyNotificationDuration = @@ -955,6 +961,7 @@ void save(const std::filesystem::path& path) { } data["General"]["isPS4Pro"] = isNeo; + data["General"]["isDevKit"] = isDevKit; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["playBGM"] = playBGM; @@ -1101,6 +1108,7 @@ void saveMainWindow(const std::filesystem::path& path) { void setDefaultValues() { isHDRAllowed = false; isNeo = false; + isDevKit = false; isFullscreen = false; isTrophyPopupDisabled = false; playBGM = false; diff --git a/src/common/config.h b/src/common/config.h index 4202da88a..1025e9956 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -28,6 +28,7 @@ void setLoadGameSizeEnabled(bool enable); bool getIsFullscreen(); std::string getFullscreenMode(); bool isNeoModeConsole(); +bool isDevKitConsole(); bool getPlayBGM(); int getBGMvolume(); bool getisTrophyPopupDisabled(); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 98d587e00..8b108a654 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -38,6 +38,16 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1 bool use_extended_mem2) { const bool is_neo = ::Libraries::Kernel::sceKernelIsNeoMode(); auto total_size = is_neo ? SCE_KERNEL_TOTAL_MEM_PRO : SCE_KERNEL_TOTAL_MEM; + if (Config::isDevKitConsole()) { + const auto old_size = total_size; + // Assuming 2gb is neo for now, will need to link it with sceKernelIsDevKit + total_size += is_neo ? 2_GB : 768_MB; + LOG_WARNING(Kernel_Vmm, + "Config::isDevKitConsole is enabled! Added additional {:s} of direct memory.", + is_neo ? "2 GB" : "768 MB"); + LOG_WARNING(Kernel_Vmm, "Old Direct Size: {:#x} -> New Direct Size: {:#x}", old_size, + total_size); + } if (!use_extended_mem1 && is_neo) { total_size -= 256_MB; } From 5caab76a4557b2721898e1257356ad3b3356dd2f Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Wed, 26 Mar 2025 22:03:50 +0100 Subject: [PATCH 28/52] Implement DmaDataSrc::MemoryUsingL2 and DmaDataDst::MemoryUsingL2 (#2680) * Implement DmaDataSrc::MemoryUsingL2 and DmaDataDst::MemoryUsingL2 * Add L2 handling to the other place it's used --- src/video_core/amdgpu/liverpool.cpp | 30 +++++++++++++++++++---------- src/video_core/amdgpu/pm4_cmds.h | 2 ++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index bfe99c754..967b952c6 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -602,20 +602,25 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spansrc_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), true); - } else if (dma_data->src_sel == DmaDataSrc::Memory && + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, dma_data->SrcAddress(), dma_data->NumBytes(), true); } else if (dma_data->src_sel == DmaDataSrc::Data && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { // LOG_WARNING(Render_Vulkan, "GDS memory read"); - } else if (dma_data->src_sel == DmaDataSrc::Memory && - dma_data->dst_sel == DmaDataDst::Memory) { + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), dma_data->SrcAddress(), dma_data->NumBytes(), false); @@ -785,19 +790,24 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq } if (dma_data->src_sel == DmaDataSrc::Data && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, &dma_data->data, sizeof(u32), true); - } else if (dma_data->src_sel == DmaDataSrc::Memory && + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && dma_data->dst_sel == DmaDataDst::Gds) { rasterizer->InlineData(dma_data->dst_addr_lo, dma_data->SrcAddress(), dma_data->NumBytes(), true); } else if (dma_data->src_sel == DmaDataSrc::Data && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), &dma_data->data, sizeof(u32), false); } else if (dma_data->src_sel == DmaDataSrc::Gds && - dma_data->dst_sel == DmaDataDst::Memory) { + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { // LOG_WARNING(Render_Vulkan, "GDS memory read"); - } else if (dma_data->src_sel == DmaDataSrc::Memory && - dma_data->dst_sel == DmaDataDst::Memory) { + } else if ((dma_data->src_sel == DmaDataSrc::Memory || + dma_data->src_sel == DmaDataSrc::MemoryUsingL2) && + (dma_data->dst_sel == DmaDataDst::Memory || + dma_data->dst_sel == DmaDataDst::MemoryUsingL2)) { rasterizer->InlineData(dma_data->DstAddress(), dma_data->SrcAddress(), dma_data->NumBytes(), false); diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index e92ba17fa..ae1d32e00 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -377,12 +377,14 @@ struct PM4CmdAcquireMem { enum class DmaDataDst : u32 { Memory = 0, Gds = 1, + MemoryUsingL2 = 3, }; enum class DmaDataSrc : u32 { Memory = 0, Gds = 1, Data = 2, + MemoryUsingL2 = 3, }; struct PM4DmaData { From ae2c9a745e43a1156405ba84ebf646882a744d71 Mon Sep 17 00:00:00 2001 From: Dmugetsu <168934208+diegolix29@users.noreply.github.com> Date: Wed, 26 Mar 2025 15:50:52 -0600 Subject: [PATCH 29/52] Gui: Adding Pause button working, full screen button and labels to buttons on main window gui (#2634) * Adding names to gui buttoms and adjusting spacing. * moving refresh button to last slot. * Changing the implementation to tooltips for hover over them - qstring to detect background color. * Fixing some themes with inverted tooltip base * Suggestions / Fixes - Pause and FullScreen Buttons * Update REUSE.toml * cleaning up * Icons stuff * clang * Buttons toggle - Cleaning code - Fixing Icons * cleaning boolean * Toggle pause and play icons and label to "Resume" when paused. * Simplifying the toggles. * New icons and final Push to review * Reuse * Icon rename, adding f9 press for pause game when no gui is on without needed of debug menu * clang + reuse * clang dosent work on this part * again Clang * Last fix for review. Light theme white resume icon fix. * Proper fix for Resume icon * New Rebase * Fixed Orientation with docking issues and cleaning boxlayout code * Adding spacer to separate actions, sizeslider on top of search bar. And adding margins * Fixed Background not showing on OLED Theme * Fixing check marks * Adding all Daniel Suggestions and fixed F9 not working with debug menu open. * Clang * reverting all OLED theme changes * Final suggestions --- REUSE.toml | 4 +- src/common/config.cpp | 12 +++ src/common/config.h | 2 + src/core/devtools/layer.cpp | 43 +++++--- src/core/devtools/layer.h | 1 + src/images/controller_icon.png | Bin 9102 -> 4142 bytes src/images/fullscreen_icon.png | Bin 0 -> 2590 bytes src/images/pause_icon.png | Bin 965 -> 1972 bytes src/images/play_icon.png | Bin 1150 -> 2875 bytes src/images/refresh_icon.png | Bin 3381 -> 0 bytes src/images/refreshlist_icon.png | Bin 0 -> 3247 bytes src/images/restart_game_icon.png | Bin 0 -> 3935 bytes src/images/settings_icon.png | Bin 2219 -> 4543 bytes src/images/stop_icon.png | Bin 658 -> 1601 bytes src/qt_gui/main_window.cpp | 168 ++++++++++++++++++++++++++++-- src/qt_gui/main_window.h | 12 +++ src/qt_gui/main_window_themes.cpp | 45 ++++---- src/qt_gui/main_window_ui.h | 32 ++++-- src/sdl_window.cpp | 20 ++++ src/sdl_window.h | 2 + src/shadps4.qrc | 74 ++++++------- 21 files changed, 324 insertions(+), 91 deletions(-) create mode 100644 src/images/fullscreen_icon.png delete mode 100644 src/images/refresh_icon.png create mode 100644 src/images/refreshlist_icon.png create mode 100644 src/images/restart_game_icon.png diff --git a/REUSE.toml b/REUSE.toml index 793990bd8..ad2bc3678 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -49,8 +49,10 @@ path = [ "src/images/pause_icon.png", "src/images/play_icon.png", "src/images/ps4_controller.png", - "src/images/refresh_icon.png", + "src/images/restart_game_icon.png", + "src/images/refreshlist_icon.png", "src/images/settings_icon.png", + "src/images/fullscreen_icon.png", "src/images/stop_icon.png", "src/images/utils_icon.png", "src/images/shadPS4.icns", diff --git a/src/common/config.cpp b/src/common/config.cpp index 16d9e5724..09236f30c 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -107,6 +107,7 @@ static bool showBackgroundImage = true; static bool isFullscreen = false; static std::string fullscreenMode = "Windowed"; static bool isHDRAllowed = false; +static bool showLabelsUnderIcons = true; // Language u32 m_language = 1; // english @@ -176,6 +177,14 @@ bool getIsFullscreen() { return isFullscreen; } +bool getShowLabelsUnderIcons() { + return showLabelsUnderIcons; +} + +bool setShowLabelsUnderIcons() { + return false; +} + std::string getFullscreenMode() { return fullscreenMode; } @@ -427,6 +436,9 @@ void setVblankDiv(u32 value) { void setIsFullscreen(bool enable) { isFullscreen = enable; } +static void setShowLabelsUnderIcons(bool enable) { + showLabelsUnderIcons = enable; +} void setFullscreenMode(std::string mode) { fullscreenMode = mode; diff --git a/src/common/config.h b/src/common/config.h index 1025e9956..3a0bf252c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -26,6 +26,8 @@ bool GetLoadGameSizeEnabled(); std::filesystem::path GetSaveDataPath(); void setLoadGameSizeEnabled(bool enable); bool getIsFullscreen(); +bool getShowLabelsUnderIcons(); +bool setShowLabelsUnderIcons(); std::string getFullscreenMode(); bool isNeoModeConsole(); bool isDevKitConsole(); diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 87fd9ffb3..94b39e801 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "SDL3/SDL_log.h" #include "layer.h" #include @@ -117,22 +118,6 @@ void L::DrawMenuBar() { EndMainMenuBar(); } - - if (IsKeyPressed(ImGuiKey_F9, false)) { - if (io.KeyCtrl && io.KeyAlt) { - if (!DebugState.ShouldPauseInSubmit()) { - DebugState.RequestFrameDump(dump_frame_count); - } - } - if (!io.KeyCtrl && !io.KeyAlt) { - if (isSystemPaused) { - DebugState.ResumeGuestThreads(); - } else { - DebugState.PauseGuestThreads(); - } - } - } - if (open_popup_options) { OpenPopup("GPU Tools Options"); just_opened_options = true; @@ -381,6 +366,32 @@ void L::Draw() { visibility_toggled = true; } + if (IsKeyPressed(ImGuiKey_F9, false)) { + if (io.KeyCtrl && io.KeyAlt) { + if (!DebugState.ShouldPauseInSubmit()) { + DebugState.RequestFrameDump(dump_frame_count); + } + } else { + if (DebugState.IsGuestThreadsPaused()) { + DebugState.ResumeGuestThreads(); + SDL_Log("Game resumed from Keyboard"); + show_pause_status = false; + } else { + DebugState.PauseGuestThreads(); + SDL_Log("Game paused from Keyboard"); + show_pause_status = true; + } + visibility_toggled = true; + } + } + + if (show_pause_status) { + ImVec2 pos = ImVec2(10, 10); + ImU32 color = IM_COL32(255, 255, 255, 255); + + ImGui::GetForegroundDrawList()->AddText(pos, color, "Game Paused Press F9 to Resume"); + } + if (show_simple_fps) { if (Begin("Video Info", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | diff --git a/src/core/devtools/layer.h b/src/core/devtools/layer.h index 5bb53fbdb..9e949c8e9 100644 --- a/src/core/devtools/layer.h +++ b/src/core/devtools/layer.h @@ -19,6 +19,7 @@ public: static void SetupSettings(); void Draw() override; + bool show_pause_status = false; }; } // namespace Core::Devtools diff --git a/src/images/controller_icon.png b/src/images/controller_icon.png index 40c92a89bc53a9447d223c3c45286afe3b3d1673..0d5556329e55f8b1854821536a58a1ecd4d4528b 100644 GIT binary patch literal 4142 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 UN}CF^{5$l5tgc(4fqtp~1!=YDDgXcg literal 9102 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 DoToWu diff --git a/src/images/fullscreen_icon.png b/src/images/fullscreen_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..719ffe4a19b842f711a4cd6be61fcbfb668b6ef1 GIT binary patch literal 2590 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 literal 0 HcmV?d00001 diff --git a/src/images/pause_icon.png b/src/images/pause_icon.png index 5375689b7880bb46d843eca0f8494e66bee69f19..86bbc6acbd15eecfa96376cfe90511a7be46387d 100644 GIT binary patch literal 1972 zcmdT_i!arfy{iVAzJ9Gbo`tMNHA#@h>IfA7M$xv8To!iOS+ z!uBEnXs6+WaHM18#mo5Cow10st?dRo|D_QOsqwN!1e@q@-_j~Y7?#6qD};vME15Pv z(U|W@K^UBosebu>(?`bl49=V?w=HGt;NW_cMJ+i6_crQ@-wqeGY$TqDRFw)lCtBX~ zR-ZQ$3+PYG-}J4~cs!d7sm0td?I($U?R3sshp$b6G-u9=L8@n!P(%_Efm0xNq)8;9 z_0IAofiHTs*LrT1*0mYCWnVZ`yvE;**s2@xP!YWk?bvKgN$F=d;Injx$1aq9I4gy6}J(8 z`xSOLdBty+xCTAIoUyF!u!C>d9+=0XtmEBHmFI_5va!dr5~Ou&QD2$PHv`eNZ-Y~p zpN+C9f?{8XLsHZBT~8;tC?AD1r2R^D?pK5*=7aeIIO>5Q7sbptUeEq1)1hT7& z=Vs?@I3x*M(H%G^piVk5>zdC|P&cw)C4EsE;hq;k+1?pcTcp(MKJvibGsk#UOi4-K z<5}xToD55I8~B|(bm{%^S8!R|@%G9Ho#*cHCSP)cUnTK8UJ$aHkOz|`>}6d|re9jK zMM;A2deITR(nQqlwk9vWK8pZVq+hk_W$q7fsgpfFy_C^7vLGT?wQrv3EKgWB}q zfZe=4&c!kRNcK7U>s#y}VkWuEW<_-h!n)rTamnb7&fiPE_8Qew&AfBatE@0%^-l#x z$)mZ)MYJbzKF!p7;f#zO+C`?YSiiwI0=K7O27$sz}M(aUznWS^rI^g z_$#AppVG~K)b8S3u~YeH^giqUpj<>3 z@$5z$_g&Pal_hb%gx)0zYj}B8#V*#ISWIN`u0X|af*hF-1^vOkz<)vOBa7kLV`-Jf zIYBrpeCZ0u^A}AnJsbB%F$Vi;n;H)FJF`0SPUe&J`V_vJnN#7N3mzG>Fx4&w%N2_u z3t1Z!hnG&X(_-8tdIPFh_oppJMZ`)yl=h5kC@36hV1_DuB4cMs^0gSyz2;jiiY2W?XKr*L~&n`rOg yq4X_mcVRF$pQf;`3y&P1L&}yT-L$My4=tU-d1msLTBv`w>9f#KDkjb=i$8av~#YX{;OV|SJ{5L_TAEltb!0QbM7;y zxd$W{u}{pdTEZO}P* z=XB%hfAdnG|Kpj%CUzky_kag0|CjGy1vAzczRNw3!CSna!R(UK>%aW#0xy4Eyi7cG zy8Z5TJMDkOUs>|(>X&mFe;&`>wCK>zaK`yUXKd&EDt_wH)SmU@by{ojeJ_jehC)@r z1}9hyICxAdZ5fVnpPS7zEzF?!o9n~ygSE&0>YP1iYjIY*DO_dK)`Skl`K=M%jn%Br zZkAFq2JMXPm#^KX2PV@1u2pis@n4=i3V-r0m>{- zIU2ml$!l8Uq|^sf)>L?fd7Lx~*u*nulBoYlF3m_bpiJW z`Re&gZ|-bg7FqdaQcIsWPte!W#fNrQ3!kf;YA{&@cD64BQY zxBmzo-6i+Rdc)?nowu{&bS$6Qzlcs^{;;L3y^22vnA~Llv4?-Xqg=?&+KN6{`R67{1=U l`|swmAL-)I{E#;Lew559d%-E~fxvvp;OXk;vd$@?2>?c3y|VxS diff --git a/src/images/play_icon.png b/src/images/play_icon.png index 2815be39d8594b213fad22e68c7e8ac16872d639..d50d404b79bc4b5f5e7122e2f30cb5663ad3c4ad 100644 GIT binary patch delta 2848 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 LNkvXXu0mjf_p(8M diff --git a/src/images/refreshlist_icon.png b/src/images/refreshlist_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7de6685b27e195b1517d5058f11e48b079f4ee08 GIT binary patch literal 3247 zcmZ8jXHXMbw++=$4M`{w5rXs@LQ~+<5=ua&m(W{~UZZpo1Po23Dbj+Wh=4Q!0Vx*@ zND~WHxClg%B1lsR65z%AeKYUPJ2UI7waeLi&z$q)q?%td;$lC?4gdhSFve(0I(z*A zkd?lNQ1Yzl#1L+2qz|YYJHJLZn6K+y)dK(;?{m=HS?D%fh%q)C0O0=n4;cKWQrYNE z0la}d-YVD|AK@N`1DLz}2H_R18CZ*`Dkv$a(LTJXr{}cd>@nULj0^xsH&_7Y7+3%d zbY((^;D7a>X`~pK{`E7`wGsm(07Pd8I(+Xk|Lgao>p!dKCIeuV>3{kEH3iT^56%FX z>6r+mGJ>J>KUM#9WGVemrChrHvl!`h8UM|+ynKnS{(DLZf%p>@U!-Xj-tf5%0C2cq z(0bO9E}O+?YE^7_-{?TmdKDP{HK1jpiUB7hE0EoUtqTRHfH0neGlQ)4^UQU4pjj^E z(gwO~Pwdt}8d4%vQiSBw;Qiu*;;!JEH`jl3ema!w=`4m%MX ze&ju;$F0UvYArQ4ADbJMsj(VQAyR@V20Mw7Seg;k_5)e8cN!!}s3#ZH{V^V;1*Yf4sPNO^HA-mc?R=D%?GzZn$Jlf<#! zhD=G?J(G{$5}8{)d^;uXfk!xQtyZOF*kjI1o{0A0qfxP@xuh<1BttX|YBZZ|J6~XS zUiL&jw!k88OOAc*RUZ-hgU1#6Jo^`JrvW$~9Ft|bz_&Cs#;crayQY#(&D{2-CfGZA zeI!xd^$`i5^=mdrfqxlMo4s_0L^N7%Nz&L9_@3`_$QtHX@~2wQI#3n0WmYLIG7i*?~NvFB2N+>A3kzXIKbFX>r7PiEds za)u~C%Sc3yS6kEl*U4=|MNC`KIDl2f>gQ}K5keEe-<@gP3lsY}I^&9_=}jako#DOq zbWT(8qSA#!+zO7=Epkf?@kUsp1awSRRph;uJIwSq;Ah3E)GI_J_gsPQB!_Ry-Am8* zkYU?PW>E-@Y@l@mg{d%2rNee6BBU=s;gQY1S{ToN2l_WqSs$}vZlh%6~YbX>oT0FJT79t zGd)sQeG2HqV_jl6STP1kJ1-QXuatB95OP}JxghWd^kmMIRtDr7y3rZ0auvEf&yuM= zKlhy_^U<1sUfUImILr62(rH^MC3eD@Bb(5Ni{0Y{aG2o=&e~)f@tIc$6ICNuzXcox zoM?prv3>CJR$gz)j91hyi(713j zWf4O&SXpk{7IRQB%rQW;+fA>Z#a*sy@|j>-&j0&04fLZB=I6YJW-W%}gLj0(<@dzE z{AzwBXb(>NklWJ1S+y5N1i!v#9lmNcrQ42PmWKzHI^ESu2umF<{3F$|jLYCWb<{&> zsD;GKv^L%L?Qrr-zR4j`t*r*Bf25^nQ%cp$>#Ss&Sck=Bg4uTbn3hefVX(wm-S%v2 z+Z2&%L~ZLyENd6s+rMSK45>_p+PdR|v2o9B*ln`lgveS!<{hk4nT?4}9{eigMc(84 zsb4Y%brL z73jQ8!OWL|P`AZ?xfo3Kr+)E1SIP1zSmIe0$+JC6SKPmpBm36_-6J0Q+Z+NKzBTrg zJZ**bjSq2i>wdF@#`Y_gzmTd!s#T$2siSG|q$u@=hR80*5I-+%{Zs zM!k1E0fsX;RwJ4@b?<3Hv#_1>0Q@O%As>uL;BOdxe#bs1Khx(*Gd!u@BU!-|JKevc z9#Ric83p%EVNPZ_n~;jOM14nKywGfBlPb~e{a|+7OiOg{&ydPA2vV@d9S2NNjdz~G zSi@b#!2_7v4mP6emBL*C&RHp0a24-X@$AsHD*cW9MgBQ5+R~qQ2(<1^W5$xxKB0Z7 z{*_ouM13pIiNpD6O9R0h8_IGbhQ))d2w(KU!@6%;iDbRn-P^D-nMU77sTkc`aYGLD zN62<{Qil(7!_(=2Z%@BRo|?AK=RWQE1hc;F;D2KYl3LL)E~U_sqzBUT5A}G-a*DmF zQqvTw$`j!~?WI1};p5VXU%|a>Fn9O|x*_@z7$G*VbyT8v1PX#gG;9-}RmTWQE<}lR zG;ov7;V=;cqRgccs>0+|B=CjsRSJ$L-Mw3J|KY&vQT8qdq_QdH_l-uKFtZ(L8P`XRyY_M&OAd_KnC2q}tTc8@NuHdUTZrm2XAd-vkbb8Tw z(7@jDul$Uq`H*&@x$7lj9^`AQRs5!<%2on<$u?%?9B;_WzkSacByqj9GmD4HAv5J= z^L!>~t3ulylT+|qn2Co}CyB_d!BNSORheDU{CEE=dS&a*RPZ_x*un$CS_-7YTtA525l&BB#(7! zMoX#5cLeObYF~0q--#=<98_P=PkG4y55)6Mh=_3vrar?$HBefWktEi-t9S<721J^7O)^*H?2;=N9}l|glp{fdA}=C z95>eS&zm$AV>jSipivSkCs)k~6kVC? vR6vQwLI1A>u$o!%&6HV+?}utw0dg4yRcpaZDklW}`E_FquA%Gn-4g!?{{X<~ literal 0 HcmV?d00001 diff --git a/src/images/restart_game_icon.png b/src/images/restart_game_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1e549e10192135af9d1ba30522c3a405521a2259 GIT binary patch literal 3935 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 literal 0 HcmV?d00001 diff --git a/src/images/settings_icon.png b/src/images/settings_icon.png index c88cd7a6f16cbc54bbb44730d3dac206f3affe2b..81127bfa3e7856ac86bb645a79a905c4324d42b3 100644 GIT binary patch literal 4543 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 r0w|%4^SL>bXBXW&WwtaSTv9Aq5DDXYMx@8o?^} delta 2190 zcmV;92yyqnBdZaRB#}WBe+XSkL_t(|ob8=iY*s}S$N&8*%3j290Dc3|W|=7O4ghZe7#K#!ju8O z9tID6bEfxd6Zb zq?{Q5&gL}uJa0#DG0cBGfCfe9H3R5pArqC^B5a-e8PH;3xIb3Ob0NTU;SJ>j>Fk+olx`@8bA&P9ZeTc^A_|7fJ^+pfATu91i;;VQcnunf7af^&Jo|TEZG4#!m>W2IR8AQ z>4R|zwgaCFf_#d?VY|=|JChPZvN4<~FvHJ40DlYpL2tZwz7hm@9)-sXLVtYWyYjn) z%8vr*jAZBmU@F^9-s7`S75_|jjwuNq(mD(JH%b$HR=!%O_(`7?11JVClh>$AhC8EL z0Of-If6Nuq0#CLbFLV{=OWJ#P@_Yb2Y`8^L**-|E?~6{tne3D!O@pzWo6>7OD#U+| z3Y%jLMEkUf4~9N?U8vWK{eFj{GSZ?B_E6toR*kngP6t^#?V2HiM%l*ntmTZ*NK)Dj zbvzi29tI ziykiLEH-TDGTsr{vRPApGwX`RD0WX7w%}C?rrT^H{gf?Naa&FLd|$muI3r6~&sZ?JmnHrbJ^CKWOpr zf4pMQ{wD6LMCikZE=eFdybX&`=1k3-5of+H(Yz^9)cqq24yLt^4QocwYxo?13xZ%Z z4m=E-BTiE)ZWX3m>OfPZ2k`>;Ns=an|8cLd%bczDg`x*iSBEtaMn1@ zeUS?02u}^|&U8!_@{e&S=QyFB`(v&he~%+&s18PSgVxsDy~(j+FeCVnks4jcE=69l zHl~gjg;TMP6$x71b}8ywZje{CyQBvyb*Nl|$iG*|21VYr9^_Q5BXq9NabA)4AzVfS zLJrBc15%lww@jP?p|+=vFlD8TeTt`t)b=_m6nT%QY*F8-$V(fVRPjVi70MJLfB#sA za*mI=?S0wtWg;(37HlOgh6ls$f@kAehqA60PT1WH?H;Ow;Ti6Wyy04PQY#Fq=?yH2 zQG)6uikzRMQFUUXT%;%d<_Kk$3uV0dDrtw2 zo0h~h;chZ$WF;#KRVLi=ynyQ-9`bgyrI@|u^Jc;|adL36_3O!2Jd=i=VBJ*K$$_S| z-wpHDJGu~*Hzx<38xU;?T`@@GFB>;CIKDNT7dN1~gdODu8O;ASsov8Sf9Av;6r(*%}<{ zL)B=Cg5Z-3dxz8-ULd`|C-4~wTt`@#kLw7!>ue1PUfAi*%D@V?^ljj@oVB+18S187 z&_&~iOj~fTd?{3JqQbt?e^?~+2WbfMdL_CLFMXaC`h%_jCc+*;h;S_$#@;s0n4nou zJ6hl=P}sA)&^F|#HBPa$aTzP)d@sPcP+gO_?tM8|_F7oR8kV_V#DzTZzyZ)G*K$K^ zjOMe%#Ryx}@&x2BkFb?)A6nx!O=u0e9Gf`T8U&x&YuMn9(w3Mue-ITVF$4YUg3mPO zEBFl9AF0h_I$#-RQAPg<0cUKJO<+x@m|R?mllHNeATHfF>YGnleK_`69nyR^^aGE< znO#0ONgYOw_T#3PEf4c|qRM!OGoROsW+6{2$KA4JtU(aWEgX$v63g~>#2}ahF1IkT zyLo<9ZEHtrI)D~pe_LuihlzE0Wx{J9<#Vi%9FfvM%12`cQvN5+ft0h{0AL=ZRH{0STY0$P*Qc0il??Q8}O}s}M~E z*343LB7MFnD%nV*?bD+kt3LiUK(dU>D80m@ya|b3SmN zI<4&GOmF_00R=98m7Nn-a#+C4{PQyY+XDVy$`c3#0)apv5C{YU0d&m2p@aE8r}ynE Q00000NkvXX1g=70f&tk8-T(jq diff --git a/src/images/stop_icon.png b/src/images/stop_icon.png index 74c615f65a54bd8380a3e67f689d86038019edc6..55b6b01c7c4c20ce586a0145ccbbfe7db38cfc43 100644 GIT binary patch literal 1601 zcmeAS@N?(olHy`uVBq!ia0vp^DImQL70(Y)*K0-phSslL`iUdT1k0gQ7S_~VrE{6o}X)of~lUN zo@wfhX`(y)37&w3&Rt70XRt82O%L|C5p=^+AG#Ht|;!HrcAtMum0FaIX z;>>myuy_`b4FaB$j0`WB5j2{m3_z&~>vp@3Xlx~Oh9v) zz$$|*Er2YjE<*zYknFd0hqp*vSLz3b#0O6o$B>G+w{yK$gr-Wgm4|PheXBJNS zg{G8*|NsAAllt+s$544k!o%~PJALkHT|a61 zNyzf;`(OJD{QfDQKQXz;@1Lf6(j+CJLt&N6_xRpZSU*AgiR+~=RsT=!f3p75mN`@B zpEx{eeg9Hlh47PSi@NWqy`RLAc+A=0_|w~;oPWMNbbVRc(i)F_`jL~$s_suVo;1tv z=+RHCKc}9lZI~OA^>fdXZINEp_L_FO@)ONJvToK{GtK;T_ESSoVFR&qJlj_4r|PBd zE_tqc`})+PhC2)E`fCF1CcHc5oc(?KljdEweudS4_g1}|A#D*{c6#y4yvB!2pH9n7 z)?d2YXiI_p$@eP1AD#acfAgt>h4{K@IiA-~hE8&C3bfJuesY(poZf@7pF8&G-+lV< z@ck+Jr}}Pblq*a^)#z1|HQ95I}0BA-%zNO4QeuO z&VAKgl77o!x!2hhv7A|cZ-vh~Tsw98_w-1M1D3{7U(cyIem|)_`TgZsxj5&Klguvi zw6ssXlx*wWm)FI7$A`^yaoJMy{a60X`u8+JK_%2`MP}yoFF|V_2Uus#Xr6VQzvHY! zMQ>16tYyuM4QEP1`q?a3RQ}~!E$Q)+U2E6BIU7ZeU0t`X)>uH8fnA7asG*)Tg|+|L z>x~W9s{5NTOPW7Ev^ulu*x9s|>f)x|e`f2Pot|WNf05zBWu{lwnM{~5>-y#HS&e@b z{=bOVYucE{qp9a-Y33Rs3zVERb=LnI`(=2ZO!>37e&*AvlPunCYO~4$R_jg*T%&qp z=DxS_d~8PQ{@?XC8{D2+xadrk>DwKyLB?6(O|yc3yBG^hJNJB(NpG*??lWbz5k?0u zX)op3dQ(NRd7tyNHWA~hsk5#te{EyGWBm4qn`M^h9ev4`6B}H%Svt9%6R(R8Uz_D1 z@%_~DCwi7${ZFEc%$vlIy9a8|m_x5Hj%{^aAA?sB}CnXb}Y`cq-|pL5>l z#Fnk*J8;jO=XATlkF;s`vofdkuYdabljzPDhRWg`1#$%s3Qky0dD{H>Tjts>q)s;Pb7n*6h+re$$!H6bLNxZl}X=?J*+FvB`m+GA3yobV*S1I zynHT+UObdLwfw|w)ybbM(>-K3yX!e)TdY1Eo7N{>DSJrRg1=VYyKKg;t+GeAzDXp00i_>zopr E0K^za>cWLdxmJqeaYUez~9^NjLra`DY8Sp;&0&QkN;qmtC9m^~iQ58_A_Neb2c6`26F&neh9wX8V@~tzW;(G}W2c zV$IJF8*WT}W%zvk+R3x{_4QmgG&`1`Hd{(ZYX zo~-abb2xIc_?6j@PP7%qypfAClUr`HE-8-JdPYH@YJY9&fr;+FOpnE$JACjEW5gWi zrSB!eE%{l08wvAA&-oIU`?=!$*IBc+T$q1+es!qgW6giFvT7LrzZTvs^#5YJ`tvQR mi{; #include #include @@ -132,23 +134,160 @@ void MainWindow::CreateActions() { m_theme_act_group->addAction(ui->setThemeOled); } +void MainWindow::PauseGame() { + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = SDL_EVENT_TOGGLE_PAUSE; + is_paused = !is_paused; + UpdateToolbarButtons(); + SDL_PushEvent(&event); +} + +void MainWindow::toggleLabelsUnderIcons() { + bool showLabels = ui->toggleLabelsAct->isChecked(); + Config::setShowLabelsUnderIcons(); + UpdateToolbarLabels(); + if (isGameRunning) { + UpdateToolbarButtons(); + } +} + +void MainWindow::toggleFullscreen() { + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = SDL_EVENT_TOGGLE_FULLSCREEN; + SDL_PushEvent(&event); +} + +QWidget* MainWindow::createButtonWithLabel(QPushButton* button, const QString& labelText, + bool showLabel) { + QWidget* container = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(container); + layout->setAlignment(Qt::AlignCenter | Qt::AlignBottom); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(button); + + QLabel* label = nullptr; + if (showLabel && ui->toggleLabelsAct->isChecked()) { + label = new QLabel(labelText, this); + label->setAlignment(Qt::AlignCenter | Qt::AlignBottom); + layout->addWidget(label); + button->setToolTip(""); + } else { + button->setToolTip(labelText); + } + + container->setLayout(layout); + container->setProperty("buttonLabel", QVariant::fromValue(label)); + return container; +} + +QWidget* createSpacer(QWidget* parent) { + QWidget* spacer = new QWidget(parent); + spacer->setFixedWidth(15); + spacer->setFixedHeight(15); + return spacer; +} + void MainWindow::AddUiWidgets() { // add toolbar widgets QApplication::setStyle("Fusion"); - ui->toolBar->setObjectName("mw_toolbar"); - ui->toolBar->addWidget(ui->playButton); - ui->toolBar->addWidget(ui->pauseButton); - ui->toolBar->addWidget(ui->stopButton); - ui->toolBar->addWidget(ui->refreshButton); - ui->toolBar->addWidget(ui->settingsButton); - ui->toolBar->addWidget(ui->controllerButton); - ui->toolBar->addWidget(ui->keyboardButton); + + bool showLabels = ui->toggleLabelsAct->isChecked(); + ui->toolBar->clear(); + + ui->toolBar->addWidget(createSpacer(this)); + ui->toolBar->addWidget(createButtonWithLabel(ui->playButton, tr("Play"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->pauseButton, tr("Pause"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->stopButton, tr("Stop"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->restartButton, tr("Restart"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); + ui->toolBar->addWidget(createButtonWithLabel(ui->settingsButton, tr("Settings"), showLabels)); + ui->toolBar->addWidget( + createButtonWithLabel(ui->fullscreenButton, tr("Full Screen"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); + ui->toolBar->addWidget( + createButtonWithLabel(ui->controllerButton, tr("Controllers"), showLabels)); + ui->toolBar->addWidget(createButtonWithLabel(ui->keyboardButton, tr("Keyboard"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); QFrame* line = new QFrame(this); - line->setFrameShape(QFrame::StyledPanel); + line->setFrameShape(QFrame::VLine); line->setFrameShadow(QFrame::Sunken); + line->setMinimumWidth(2); ui->toolBar->addWidget(line); - ui->toolBar->addWidget(ui->sizeSliderContainer); - ui->toolBar->addWidget(ui->mw_searchbar); + ui->toolBar->addWidget(createSpacer(this)); + if (showLabels) { + QLabel* pauseButtonLabel = ui->pauseButton->parentWidget()->findChild(); + if (pauseButtonLabel) { + pauseButtonLabel->setVisible(false); + } + } + ui->toolBar->addWidget( + createButtonWithLabel(ui->refreshButton, tr("Refresh List"), showLabels)); + ui->toolBar->addWidget(createSpacer(this)); + + QBoxLayout* toolbarLayout = new QBoxLayout(QBoxLayout::TopToBottom); + toolbarLayout->setSpacing(2); + toolbarLayout->setContentsMargins(2, 2, 2, 2); + ui->sizeSliderContainer->setFixedWidth(150); + + QWidget* searchSliderContainer = new QWidget(this); + QBoxLayout* searchSliderLayout = new QBoxLayout(QBoxLayout::TopToBottom); + searchSliderLayout->setContentsMargins(0, 0, 6, 6); + searchSliderLayout->setSpacing(2); + ui->mw_searchbar->setFixedWidth(150); + + searchSliderLayout->addWidget(ui->sizeSliderContainer); + searchSliderLayout->addWidget(ui->mw_searchbar); + + searchSliderContainer->setLayout(searchSliderLayout); + + ui->toolBar->addWidget(searchSliderContainer); + + if (!showLabels) { + toolbarLayout->addWidget(searchSliderContainer); + } + + ui->playButton->setVisible(true); + ui->pauseButton->setVisible(false); +} + +void MainWindow::UpdateToolbarButtons() { + // add toolbar widgets when game is running + bool showLabels = ui->toggleLabelsAct->isChecked(); + + ui->playButton->setVisible(false); + ui->pauseButton->setVisible(true); + + if (showLabels) { + QLabel* playButtonLabel = ui->playButton->parentWidget()->findChild(); + if (playButtonLabel) + playButtonLabel->setVisible(false); + } + + if (is_paused) { + ui->pauseButton->setIcon(ui->playButton->icon()); + ui->pauseButton->setToolTip(tr("Resume")); + } else { + if (isIconBlack) { + ui->pauseButton->setIcon(QIcon(":images/pause_icon.png")); + } else { + ui->pauseButton->setIcon(RecolorIcon(QIcon(":images/pause_icon.png"), isWhite)); + } + ui->pauseButton->setToolTip(tr("Pause")); + } + + if (showLabels) { + QLabel* pauseButtonLabel = ui->pauseButton->parentWidget()->findChild(); + if (pauseButtonLabel) { + pauseButtonLabel->setText(is_paused ? tr("Resume") : tr("Pause")); + pauseButtonLabel->setVisible(true); + } + } +} + +void MainWindow::UpdateToolbarLabels() { + AddUiWidgets(); } void MainWindow::CreateDockWindows() { @@ -253,6 +392,8 @@ void MainWindow::CreateConnects() { connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable); connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList); connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable); + connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons); + connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen); connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { if (isTableList) { @@ -276,6 +417,7 @@ void MainWindow::CreateConnects() { }); connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame); + connect(ui->pauseButton, &QPushButton::clicked, this, &MainWindow::PauseGame); connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this, &MainWindow::StartGame); connect(m_game_list_frame.get(), &QTableWidget::cellDoubleClicked, this, @@ -743,6 +885,8 @@ void MainWindow::StartGame() { return; } StartEmulator(path); + + UpdateToolbarButtons(); } } @@ -1217,7 +1361,9 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->pauseButton->setIcon(RecolorIcon(ui->pauseButton->icon(), isWhite)); ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite)); ui->refreshButton->setIcon(RecolorIcon(ui->refreshButton->icon(), isWhite)); + ui->restartButton->setIcon(RecolorIcon(ui->restartButton->icon(), isWhite)); ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite)); + ui->fullscreenButton->setIcon(RecolorIcon(ui->fullscreenButton->icon(), isWhite)); ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite)); ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->icon(), isWhite)); ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 5ac56e44c..bcd5e53ba 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "background_music_player.h" @@ -38,6 +39,8 @@ public: void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg); void InstallDirectory(); void StartGame(); + void PauseGame(); + bool showLabels; private Q_SLOTS: void ConfigureGuiFromSettings(); @@ -47,15 +50,21 @@ private Q_SLOTS: void RefreshGameTable(); void HandleResize(QResizeEvent* event); void OnLanguageChanged(const std::string& locale); + void toggleLabelsUnderIcons(); private: Ui_MainWindow* ui; void AddUiWidgets(); + void UpdateToolbarLabels(); + void UpdateToolbarButtons(); + QWidget* createButtonWithLabel(QPushButton* button, const QString& labelText, bool showLabel); void CreateActions(); + void toggleFullscreen(); void CreateRecentGameActions(); void CreateDockWindows(); void GetPhysicalDevices(); void LoadGameLists(); + #ifdef ENABLE_UPDATER void CheckUpdateMain(bool checkSave); #endif @@ -73,6 +82,9 @@ private: bool isIconBlack = false; bool isTableList = true; bool isGameRunning = false; + bool isWhite = false; + bool is_paused = false; + QActionGroup* m_icon_size_act_group = nullptr; QActionGroup* m_list_mode_act_group = nullptr; QActionGroup* m_theme_act_group = nullptr; diff --git a/src/qt_gui/main_window_themes.cpp b/src/qt_gui/main_window_themes.cpp index c5574fca9..624673cba 100644 --- a/src/qt_gui/main_window_themes.cpp +++ b/src/qt_gui/main_window_themes.cpp @@ -19,7 +19,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); themePalette.setColor(QPalette::Base, QColor(20, 20, 20)); themePalette.setColor(QPalette::AlternateBase, QColor(53, 53, 53)); - themePalette.setColor(QPalette::ToolTipBase, Qt::white); + themePalette.setColor(QPalette::ToolTipBase, QColor(20, 20, 20)); themePalette.setColor(QPalette::ToolTipText, Qt::white); themePalette.setColor(QPalette::Text, Qt::white); themePalette.setColor(QPalette::Button, QColor(53, 53, 53)); @@ -37,18 +37,18 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { "border-radius: 4px; padding: 5px; }" "QLineEdit:focus {" "border: 1px solid #2A82DA; }"); - themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray - themePalette.setColor(QPalette::WindowText, Qt::black); // Black - themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish - themePalette.setColor(QPalette::ToolTipBase, Qt::black); // Black - themePalette.setColor(QPalette::ToolTipText, Qt::black); // Black - themePalette.setColor(QPalette::Text, Qt::black); // Black - themePalette.setColor(QPalette::Button, QColor(240, 240, 240)); // Light gray - themePalette.setColor(QPalette::ButtonText, Qt::black); // Black - themePalette.setColor(QPalette::BrightText, Qt::red); // Red - themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); // Blue - themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Blue - themePalette.setColor(QPalette::HighlightedText, Qt::white); // White + themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray + themePalette.setColor(QPalette::WindowText, Qt::black); // Black + themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish + themePalette.setColor(QPalette::ToolTipBase, QColor(230, 230, 230, 80)); // Grayish + themePalette.setColor(QPalette::ToolTipText, Qt::black); // Black + themePalette.setColor(QPalette::Text, Qt::black); // Black + themePalette.setColor(QPalette::Button, QColor(240, 240, 240)); // Light gray + themePalette.setColor(QPalette::ButtonText, Qt::black); // Black + themePalette.setColor(QPalette::BrightText, Qt::red); // Red + themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); // Blue + themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Blue + themePalette.setColor(QPalette::HighlightedText, Qt::white); // White qApp->setPalette(themePalette); break; case Theme::Green: @@ -62,8 +62,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(25, 40, 25)); // Darker green base themePalette.setColor(QPalette::AlternateBase, - QColor(53, 69, 53)); // Dark green alternate base - themePalette.setColor(QPalette::ToolTipBase, Qt::white); // White tooltip background + QColor(53, 69, 53)); // Dark green alternate base + themePalette.setColor(QPalette::ToolTipBase, + QColor(25, 40, 25)); // White tooltip background themePalette.setColor(QPalette::ToolTipText, Qt::white); // White tooltip text themePalette.setColor(QPalette::Text, Qt::white); // White text themePalette.setColor(QPalette::Button, QColor(53, 69, 53)); // Dark green button @@ -85,8 +86,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(20, 40, 60)); // Darker blue base themePalette.setColor(QPalette::AlternateBase, - QColor(40, 60, 90)); // Dark blue alternate base - themePalette.setColor(QPalette::ToolTipBase, Qt::white); // White tooltip background + QColor(40, 60, 90)); // Dark blue alternate base + themePalette.setColor(QPalette::ToolTipBase, + QColor(20, 40, 60)); // White tooltip background themePalette.setColor(QPalette::ToolTipText, Qt::white); // White tooltip text themePalette.setColor(QPalette::Text, Qt::white); // White text themePalette.setColor(QPalette::Button, QColor(40, 60, 90)); // Dark blue button @@ -109,8 +111,9 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, Qt::white); // White text themePalette.setColor(QPalette::Base, QColor(80, 30, 90)); // Darker violet base themePalette.setColor(QPalette::AlternateBase, - QColor(100, 50, 120)); // Violet alternate base - themePalette.setColor(QPalette::ToolTipBase, Qt::white); // White tooltip background + QColor(100, 50, 120)); // Violet alternate base + themePalette.setColor(QPalette::ToolTipBase, + QColor(80, 30, 90)); // White tooltip background themePalette.setColor(QPalette::ToolTipText, Qt::white); // White tooltip text themePalette.setColor(QPalette::Text, Qt::white); // White text themePalette.setColor(QPalette::Button, QColor(100, 50, 120)); // Violet button @@ -133,7 +136,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, QColor(249, 245, 215)); themePalette.setColor(QPalette::Base, QColor(29, 32, 33)); themePalette.setColor(QPalette::AlternateBase, QColor(50, 48, 47)); - themePalette.setColor(QPalette::ToolTipBase, QColor(249, 245, 215)); + themePalette.setColor(QPalette::ToolTipBase, QColor(29, 32, 33)); themePalette.setColor(QPalette::ToolTipText, QColor(249, 245, 215)); themePalette.setColor(QPalette::Text, QColor(249, 245, 215)); themePalette.setColor(QPalette::Button, QColor(40, 40, 40)); @@ -155,7 +158,7 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) { themePalette.setColor(QPalette::WindowText, QColor(192, 202, 245)); themePalette.setColor(QPalette::Base, QColor(25, 28, 39)); themePalette.setColor(QPalette::AlternateBase, QColor(36, 40, 59)); - themePalette.setColor(QPalette::ToolTipBase, QColor(192, 202, 245)); + themePalette.setColor(QPalette::ToolTipBase, QColor(25, 28, 39)); themePalette.setColor(QPalette::ToolTipText, QColor(192, 202, 245)); themePalette.setColor(QPalette::Text, QColor(192, 202, 245)); themePalette.setColor(QPalette::Button, QColor(30, 30, 41)); diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index 246c2afd6..c4f47b636 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -20,6 +20,7 @@ public: QAction* setIconSizeSmallAct; QAction* setIconSizeMediumAct; QAction* setIconSizeLargeAct; + QAction* toggleLabelsAct; QAction* setlistModeListAct; QAction* setlistModeGridAct; QAction* setlistElfAct; @@ -50,6 +51,8 @@ public: QPushButton* settingsButton; QPushButton* controllerButton; QPushButton* keyboardButton; + QPushButton* fullscreenButton; + QPushButton* restartButton; QWidget* sizeSliderContainer; QHBoxLayout* sizeSliderContainer_layout; @@ -104,7 +107,15 @@ public: showGameListAct->setCheckable(true); refreshGameListAct = new QAction(MainWindow); refreshGameListAct->setObjectName("refreshGameListAct"); - refreshGameListAct->setIcon(QIcon(":images/refresh_icon.png")); + refreshGameListAct->setIcon(QIcon(":images/refreshlist_icon.png")); + + toggleLabelsAct = new QAction(MainWindow); + toggleLabelsAct->setObjectName("toggleLabelsAct"); + toggleLabelsAct->setText( + QCoreApplication::translate("MainWindow", "Show Labels Under Icons")); + toggleLabelsAct->setCheckable(true); + toggleLabelsAct->setChecked(Config::getShowLabelsUnderIcons()); + setIconSizeTinyAct = new QAction(MainWindow); setIconSizeTinyAct->setObjectName("setIconSizeTinyAct"); setIconSizeTinyAct->setCheckable(true); @@ -210,20 +221,28 @@ public: stopButton->setIconSize(QSize(40, 40)); refreshButton = new QPushButton(centralWidget); refreshButton->setFlat(true); - refreshButton->setIcon(QIcon(":images/refresh_icon.png")); - refreshButton->setIconSize(QSize(32, 32)); + refreshButton->setIcon(QIcon(":images/refreshlist_icon.png")); + refreshButton->setIconSize(QSize(40, 40)); + fullscreenButton = new QPushButton(centralWidget); + fullscreenButton->setFlat(true); + fullscreenButton->setIcon(QIcon(":images/fullscreen_icon.png")); + fullscreenButton->setIconSize(QSize(38, 38)); settingsButton = new QPushButton(centralWidget); settingsButton->setFlat(true); settingsButton->setIcon(QIcon(":images/settings_icon.png")); - settingsButton->setIconSize(QSize(44, 44)); + settingsButton->setIconSize(QSize(40, 40)); controllerButton = new QPushButton(centralWidget); controllerButton->setFlat(true); controllerButton->setIcon(QIcon(":images/controller_icon.png")); - controllerButton->setIconSize(QSize(40, 40)); + controllerButton->setIconSize(QSize(55, 48)); keyboardButton = new QPushButton(centralWidget); keyboardButton->setFlat(true); keyboardButton->setIcon(QIcon(":images/keyboard_icon.png")); - keyboardButton->setIconSize(QSize(48, 44)); + keyboardButton->setIconSize(QSize(50, 50)); + restartButton = new QPushButton(centralWidget); + restartButton->setFlat(true); + restartButton->setIcon(QIcon(":images/restart_game_icon.png")); + restartButton->setIconSize(QSize(40, 40)); sizeSliderContainer = new QWidget(centralWidget); sizeSliderContainer->setObjectName("sizeSliderContainer"); @@ -304,6 +323,7 @@ public: menuView->addAction(refreshGameListAct); menuView->addAction(menuGame_List_Mode->menuAction()); menuView->addAction(menuGame_List_Icons->menuAction()); + menuView->addAction(toggleLabelsAct); menuView->addAction(menuThemes->menuAction()); menuThemes->addAction(setThemeDark); menuThemes->addAction(setThemeLight); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index 80d196147..fcdde7240 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -11,6 +11,7 @@ #include "common/config.h" #include "common/elf_info.h" #include "common/version.h" +#include "core/debug_state.h" #include "core/libraries/kernel/time.h" #include "core/libraries/pad/pad.h" #include "imgui/renderer/imgui_core.h" @@ -396,6 +397,25 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_QUIT: is_open = false; break; + case SDL_EVENT_TOGGLE_FULLSCREEN: { + if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { + SDL_SetWindowFullscreen(window, 0); + } else { + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); + } + break; + } + case SDL_EVENT_TOGGLE_PAUSE: + SDL_Log("Received SDL_EVENT_TOGGLE_PAUSE"); + + if (DebugState.IsGuestThreadsPaused()) { + SDL_Log("Game Resumed"); + DebugState.ResumeGuestThreads(); + } else { + SDL_Log("Game Paused"); + DebugState.PauseGuestThreads(); + } + break; default: break; } diff --git a/src/sdl_window.h b/src/sdl_window.h index 03ba0797b..48a9be58c 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -7,6 +7,8 @@ #include "core/libraries/pad/pad.h" #include "input/controller.h" #include "string" +#define SDL_EVENT_TOGGLE_FULLSCREEN (SDL_EVENT_USER + 1) +#define SDL_EVENT_TOGGLE_PAUSE (SDL_EVENT_USER + 2) struct SDL_Window; struct SDL_Gamepad; diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 340756f5c..83dea01c4 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -1,38 +1,40 @@ - - images/shadps4.ico - images/about_icon.png - images/dump_icon.png - images/play_icon.png - images/pause_icon.png - images/stop_icon.png - images/utils_icon.png - images/file_icon.png - images/trophy_icon.png - images/folder_icon.png - images/themes_icon.png - images/iconsize_icon.png - images/list_icon.png - images/grid_icon.png - images/exit_icon.png - images/settings_icon.png - images/controller_icon.png - images/refresh_icon.png - images/update_icon.png - images/list_mode_icon.png - images/flag_jp.png - images/flag_eu.png - images/flag_unk.png - images/flag_us.png - images/flag_world.png - images/flag_china.png - images/github.png - images/discord.png - images/ko-fi.png - images/youtube.png - images/website.png - images/ps4_controller.png - images/keyboard_icon.png - images/KBM.png - + + images/shadps4.ico + images/about_icon.png + images/dump_icon.png + images/play_icon.png + images/pause_icon.png + images/stop_icon.png + images/utils_icon.png + images/file_icon.png + images/folder_icon.png + images/themes_icon.png + images/iconsize_icon.png + images/list_icon.png + images/grid_icon.png + images/exit_icon.png + images/settings_icon.png + images/controller_icon.png + images/restart_game_icon.png + images/update_icon.png + images/list_mode_icon.png + images/flag_jp.png + images/flag_eu.png + images/flag_unk.png + images/flag_us.png + images/flag_world.png + images/flag_china.png + images/github.png + images/discord.png + images/ko-fi.png + images/youtube.png + images/website.png + images/ps4_controller.png + images/keyboard_icon.png + images/KBM.png + images/fullscreen_icon.png + images/refreshlist_icon.png + images/trophy_icon.png + From 437af9320104011b32c34c85a6d888a78969b705 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Wed, 26 Mar 2025 23:51:17 +0200 Subject: [PATCH 30/52] New translations en_us.ts (Chinese Traditional) (#2691) --- src/qt_gui/translations/zh_TW.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index bd546380a..fb42a43b0 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -407,7 +407,7 @@ ControlSettings Configure Controls - Configure Controls + 操控設定 D-Pad @@ -519,7 +519,7 @@ Color Adjustment - Color Adjustment + 色彩調整 R: @@ -543,7 +543,7 @@ Unable to Save - Unable to Save + 無法保存 Cannot bind axis values more than once @@ -551,11 +551,11 @@ Save - Save + 保存 Apply - Apply + 套用 Restore Defaults @@ -563,7 +563,7 @@ Cancel - Cancel + 取消 @@ -578,7 +578,7 @@ Error - Error + 錯誤 Could not open the file for reading @@ -590,7 +590,7 @@ Save Changes - Save Changes + 儲存變更 Do you want to save changes? From c96853816a7d0897a113352c186ff40b94ea885c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 27 Mar 2025 12:14:09 +0200 Subject: [PATCH 31/52] [ci skip] Qt GUI: Update Translation. (#2692) Co-authored-by: georgemoralis <4313123+georgemoralis@users.noreply.github.com> --- src/qt_gui/translations/en_US.ts | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index 20cba0378..d18609295 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + + + + Pause + + + + Stop + + + + Restart + + + + Full Screen + + + + Controllers + + + + Keyboard + + + + Refresh List + + + + Resume + + + + Show Labels Under Icons + + PKGViewer From 602de0c370467f75d07a7841672e8dacd09faec5 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 27 Mar 2025 21:40:15 +0100 Subject: [PATCH 32/52] Fork detection: Fix PR actions only showing HEAD as the branch name (#2697) * I'd be very surprised if this works 1st try * More logging and cleanup * Minor fixes --- CMakeLists.txt | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c0932b5c..b3d214ec9 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,28 +113,39 @@ git_describe(GIT_DESC --always --long --dirty) git_branch_name(GIT_BRANCH) string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S") +message("start git things") # Try to get the upstream remote and branch +message("check for remote and branch") execute_process( COMMAND git rev-parse --abbrev-ref --symbolic-full-name @{u} OUTPUT_VARIABLE GIT_REMOTE_NAME - RESULT_VARIABLE GIT_BRANCH_RESULT + RESULT_VARIABLE GIT_REMOTE_RESULT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) - # If there's no upstream set or the command failed, check remote.pushDefault -if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "") +if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") + message("check default push") execute_process( COMMAND git config --get remote.pushDefault OUTPUT_VARIABLE GIT_REMOTE_NAME - RESULT_VARIABLE GIT_PUSH_DEFAULT_RESULT + RESULT_VARIABLE GIT_REMOTE_RESULT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) - - # If remote.pushDefault is not set or fails, default to origin - if (GIT_PUSH_DEFAULT_RESULT OR GIT_REMOTE_NAME STREQUAL "") - set(GIT_REMOTE_NAME "origin") +endif() +# If running in GitHub Actions and the above fails +if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") + message("check github") + set(GIT_REMOTE_NAME "origin") + + if (DEFINED ENV{GITHUB_HEAD_REF}) # PR branch name + set(GIT_BRANCH "pr-$ENV{GITHUB_HEAD_REF}") + elseif (DEFINED ENV{GITHUB_REF}) # Normal branch name + string(REGEX REPLACE "^refs/[^/]*/" "" GIT_BRANCH "$ENV{GITHUB_REF}") + else() + message("couldn't find branch") + set(GIT_BRANCH "detached-head") endif() else() # Extract remote name if the output contains a remote/branch format @@ -148,6 +159,7 @@ else() endif() # Get remote link +message("getting remote link") execute_process( COMMAND git config --get remote.${GIT_REMOTE_NAME}.url OUTPUT_VARIABLE GIT_REMOTE_URL @@ -156,6 +168,8 @@ execute_process( configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) +message("end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}") + find_package(Boost 1.84.0 CONFIG) find_package(FFmpeg 5.1.2 MODULE) find_package(fmt 10.2.0 CONFIG) From d339b3f7d663ba89bb5b3cd572ead77b41b0f0a8 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Thu, 27 Mar 2025 20:40:40 +0000 Subject: [PATCH 33/52] change async to sync (#2698) --- src/common/config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 09236f30c..b113ac0ef 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -41,7 +41,7 @@ static u32 screenWidth = 1280; static u32 screenHeight = 720; static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select static std::string logFilter; -static std::string logType = "async"; +static std::string logType = "sync"; static std::string userName = "shadPS4"; static std::string updateChannel; static std::string chooseHomeTab; @@ -1129,7 +1129,7 @@ void setDefaultValues() { screenWidth = 1280; screenHeight = 720; logFilter = ""; - logType = "async"; + logType = "sync"; userName = "shadPS4"; if (Common::isRelease) { updateChannel = "Release"; From be22674f8c1ac84e1cff89947ff4a6753070f21b Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:15:31 +0200 Subject: [PATCH 34/52] code: Remove fpkg code Signed-off-by: georgemoralis --- CMakeLists.txt | 9 - src/core/crypto/crypto.cpp | 215 ---------- src/core/crypto/crypto.h | 63 --- src/core/crypto/keys.h | 305 -------------- src/core/file_format/pkg.h | 174 -------- src/core/file_format/pkg_type.cpp | 638 ------------------------------ src/core/file_format/pkg_type.h | 10 - src/core/file_format/trp.cpp | 30 +- src/core/file_format/trp.h | 4 +- src/qt_gui/main_window.cpp | 284 +------------ src/qt_gui/main_window.h | 18 - src/qt_gui/main_window_ui.h | 17 +- src/qt_gui/pkg_viewer.cpp | 217 ---------- src/qt_gui/pkg_viewer.h | 62 --- 14 files changed, 31 insertions(+), 2015 deletions(-) delete mode 100644 src/core/crypto/crypto.cpp delete mode 100644 src/core/crypto/crypto.h delete mode 100644 src/core/crypto/keys.h delete mode 100644 src/core/file_format/pkg.h delete mode 100644 src/core/file_format/pkg_type.cpp delete mode 100644 src/core/file_format/pkg_type.h delete mode 100644 src/qt_gui/pkg_viewer.cpp delete mode 100644 src/qt_gui/pkg_viewer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b3d214ec9..0359246c8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -653,9 +653,6 @@ set(CORE src/core/aerolib/stubs.cpp src/core/aerolib/aerolib.h src/core/address_space.cpp src/core/address_space.h - src/core/crypto/crypto.cpp - src/core/crypto/crypto.h - src/core/crypto/keys.h src/core/devices/base_device.cpp src/core/devices/base_device.h src/core/devices/ioccom.h @@ -673,10 +670,6 @@ set(CORE src/core/aerolib/stubs.cpp src/core/devices/srandom_device.cpp src/core/devices/srandom_device.h src/core/file_format/pfs.h - src/core/file_format/pkg.cpp - src/core/file_format/pkg.h - src/core/file_format/pkg_type.cpp - src/core/file_format/pkg_type.h src/core/file_format/psf.cpp src/core/file_format/psf.h src/core/file_format/playgo_chunk.cpp @@ -978,8 +971,6 @@ set(QT_GUI src/qt_gui/about_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 src/qt_gui/trophy_viewer.h src/qt_gui/elf_viewer.cpp diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp deleted file mode 100644 index 4020edfd8..000000000 --- a/src/core/crypto/crypto.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "crypto.h" - -CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() { - CryptoPP::InvertibleRSAFunction params; - params.SetPrime1(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime1, 0x80)); - params.SetPrime2(CryptoPP::Integer(PkgDerivedKey3Keyset::Prime2, 0x80)); - - params.SetPublicExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PublicExponent, 4)); - params.SetPrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::PrivateExponent, 0x100)); - - params.SetModPrime1PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent1, 0x80)); - params.SetModPrime2PrivateExponent(CryptoPP::Integer(PkgDerivedKey3Keyset::Exponent2, 0x80)); - - params.SetModulus(CryptoPP::Integer(PkgDerivedKey3Keyset::Modulus, 0x100)); - params.SetMultiplicativeInverseOfPrime2ModPrime1( - CryptoPP::Integer(PkgDerivedKey3Keyset::Coefficient, 0x80)); - - CryptoPP::RSA::PrivateKey privateKey(params); - - return privateKey; -} - -CryptoPP::RSA::PrivateKey Crypto::FakeKeyset_keyset_init() { - CryptoPP::InvertibleRSAFunction params; - params.SetPrime1(CryptoPP::Integer(FakeKeyset::Prime1, 0x80)); - params.SetPrime2(CryptoPP::Integer(FakeKeyset::Prime2, 0x80)); - - params.SetPublicExponent(CryptoPP::Integer(FakeKeyset::PublicExponent, 4)); - params.SetPrivateExponent(CryptoPP::Integer(FakeKeyset::PrivateExponent, 0x100)); - - params.SetModPrime1PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent1, 0x80)); - params.SetModPrime2PrivateExponent(CryptoPP::Integer(FakeKeyset::Exponent2, 0x80)); - - params.SetModulus(CryptoPP::Integer(FakeKeyset::Modulus, 0x100)); - params.SetMultiplicativeInverseOfPrime2ModPrime1( - CryptoPP::Integer(FakeKeyset::Coefficient, 0x80)); - - CryptoPP::RSA::PrivateKey privateKey(params); - - return privateKey; -} - -CryptoPP::RSA::PrivateKey Crypto::DebugRifKeyset_init() { - CryptoPP::InvertibleRSAFunction params; - params.SetPrime1(CryptoPP::Integer(DebugRifKeyset::Prime1, sizeof(DebugRifKeyset::Prime1))); - params.SetPrime2(CryptoPP::Integer(DebugRifKeyset::Prime2, sizeof(DebugRifKeyset::Prime2))); - - params.SetPublicExponent( - CryptoPP::Integer(DebugRifKeyset::PublicExponent, sizeof(DebugRifKeyset::PublicExponent))); - params.SetPrivateExponent(CryptoPP::Integer(DebugRifKeyset::PrivateExponent, - sizeof(DebugRifKeyset::PrivateExponent))); - - params.SetModPrime1PrivateExponent( - CryptoPP::Integer(DebugRifKeyset::Exponent1, sizeof(DebugRifKeyset::Exponent1))); - params.SetModPrime2PrivateExponent( - CryptoPP::Integer(DebugRifKeyset::Exponent2, sizeof(DebugRifKeyset::Exponent2))); - - params.SetModulus(CryptoPP::Integer(DebugRifKeyset::Modulus, sizeof(DebugRifKeyset::Modulus))); - params.SetMultiplicativeInverseOfPrime2ModPrime1( - CryptoPP::Integer(DebugRifKeyset::Coefficient, sizeof(DebugRifKeyset::Coefficient))); - - CryptoPP::RSA::PrivateKey privateKey(params); - - return privateKey; -} - -void Crypto::RSA2048Decrypt(std::span dec_key, - std::span ciphertext, - bool is_dk3) { // RSAES_PKCS1v15_ - // Create an RSA decryptor - CryptoPP::RSA::PrivateKey privateKey; - if (is_dk3) { - privateKey = key_pkg_derived_key3_keyset_init(); - } else { - privateKey = FakeKeyset_keyset_init(); - } - - CryptoPP::RSAES_PKCS1v15_Decryptor rsaDecryptor(privateKey); - - // Allocate memory for the decrypted data - std::array decrypted; - - // Perform the decryption - CryptoPP::AutoSeededRandomPool rng; - CryptoPP::DecodingResult result = - rsaDecryptor.Decrypt(rng, ciphertext.data(), decrypted.size(), decrypted.data()); - std::copy(decrypted.begin(), decrypted.begin() + dec_key.size(), dec_key.begin()); -} - -void Crypto::ivKeyHASH256(std::span cipher_input, - std::span ivkey_result) { - CryptoPP::SHA256 sha256; - std::array hashResult; - auto array_sink = new CryptoPP::ArraySink(hashResult.data(), CryptoPP::SHA256::DIGESTSIZE); - auto filter = new CryptoPP::HashFilter(sha256, array_sink); - CryptoPP::ArraySource r(cipher_input.data(), cipher_input.size(), true, filter); - std::copy(hashResult.begin(), hashResult.begin() + ivkey_result.size(), ivkey_result.begin()); -} - -void Crypto::aesCbcCfb128Decrypt(std::span ivkey, - std::span ciphertext, - std::span decrypted) { - std::array key; - std::array iv; - - std::copy(ivkey.begin() + 16, ivkey.begin() + 16 + key.size(), key.begin()); - std::copy(ivkey.begin(), ivkey.begin() + iv.size(), iv.begin()); - - CryptoPP::AES::Decryption aesDecryption(key.data(), CryptoPP::AES::DEFAULT_KEYLENGTH); - CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data()); - - for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { - cbcDecryption.ProcessData(decrypted.data() + i, ciphertext.data() + i, - CryptoPP::AES::BLOCKSIZE); - } -} - -void Crypto::aesCbcCfb128DecryptEntry(std::span ivkey, - std::span ciphertext, - std::span decrypted) { - std::array key; - std::array iv; - - std::copy(ivkey.begin() + 16, ivkey.begin() + 16 + key.size(), key.begin()); - std::copy(ivkey.begin(), ivkey.begin() + iv.size(), iv.begin()); - - CryptoPP::AES::Decryption aesDecryption(key.data(), CryptoPP::AES::DEFAULT_KEYLENGTH); - CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data()); - - for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { - cbcDecryption.ProcessData(decrypted.data() + i, ciphertext.data() + i, - CryptoPP::AES::BLOCKSIZE); - } -} - -void Crypto::decryptEFSM(std::span trophyKey, - std::span NPcommID, - std::span efsmIv, std::span ciphertext, - std::span decrypted) { - - // step 1: Encrypt NPcommID - CryptoPP::CBC_Mode::Encryption encrypt; - - std::vector trophyIv(16, 0); - std::vector trpKey(16); - - encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data()); - encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16); - - // step 2: decrypt efsm. - CryptoPP::CBC_Mode::Decryption decrypt; - decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data()); - - for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { - decrypt.ProcessData(decrypted.data() + i, ciphertext.data() + i, CryptoPP::AES::BLOCKSIZE); - } -} - -void Crypto::PfsGenCryptoKey(std::span ekpfs, - std::span seed, - std::span dataKey, - std::span tweakKey) { - CryptoPP::HMAC hmac(ekpfs.data(), ekpfs.size()); - - CryptoPP::SecByteBlock d(20); // Use Crypto++ SecByteBlock for better memory management - - // Copy the bytes of 'index' to the 'd' array - uint32_t index = 1; - std::memcpy(d, &index, sizeof(uint32_t)); - - // Copy the bytes of 'seed' to the 'd' array starting from index 4 - std::memcpy(d + sizeof(uint32_t), seed.data(), seed.size()); - - // Allocate memory for 'u64' using new - std::vector data_tweak_key(hmac.DigestSize()); - - // Calculate the HMAC - hmac.CalculateDigest(data_tweak_key.data(), d, d.size()); - std::copy(data_tweak_key.begin(), data_tweak_key.begin() + dataKey.size(), tweakKey.begin()); - std::copy(data_tweak_key.begin() + tweakKey.size(), - data_tweak_key.begin() + tweakKey.size() + dataKey.size(), dataKey.begin()); -} - -void Crypto::decryptPFS(std::span dataKey, - std::span tweakKey, std::span src_image, - std::span dst_image, u64 sector) { - // Start at 0x10000 to keep the header when decrypting the whole pfs_image. - for (int i = 0; i < src_image.size(); i += 0x1000) { - const u64 current_sector = sector + (i / 0x1000); - CryptoPP::ECB_Mode::Encryption encrypt(tweakKey.data(), tweakKey.size()); - CryptoPP::ECB_Mode::Decryption decrypt(dataKey.data(), dataKey.size()); - - std::array tweak{}; - std::array encryptedTweak; - std::array xorBuffer; - std::memcpy(tweak.data(), ¤t_sector, sizeof(u64)); - - // Encrypt the tweak for each sector. - encrypt.ProcessData(encryptedTweak.data(), tweak.data(), 16); - - for (int plaintextOffset = 0; plaintextOffset < 0x1000; plaintextOffset += 16) { - xtsXorBlock(xorBuffer.data(), src_image.data() + i + plaintextOffset, - encryptedTweak.data()); // x, c, t - decrypt.ProcessData(xorBuffer.data(), xorBuffer.data(), 16); // x, x - xtsXorBlock(dst_image.data() + i + plaintextOffset, xorBuffer.data(), - encryptedTweak.data()); //(p) c, x , t - xtsMult(encryptedTweak); - } - } -} diff --git a/src/core/crypto/crypto.h b/src/core/crypto/crypto.h deleted file mode 100644 index b5d8104b5..000000000 --- a/src/core/crypto/crypto.h +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common/types.h" -#include "keys.h" - -class Crypto { -public: - CryptoPP::RSA::PrivateKey key_pkg_derived_key3_keyset_init(); - CryptoPP::RSA::PrivateKey FakeKeyset_keyset_init(); - CryptoPP::RSA::PrivateKey DebugRifKeyset_init(); - - void RSA2048Decrypt(std::span dk3, - std::span ciphertext, - bool is_dk3); // RSAES_PKCS1v15_ - void ivKeyHASH256(std::span cipher_input, - std::span ivkey_result); - void aesCbcCfb128Decrypt(std::span ivkey, - std::span ciphertext, - std::span decrypted); - void aesCbcCfb128DecryptEntry(std::span ivkey, - std::span ciphertext, - std::span decrypted); - void decryptEFSM(std::span trophyKey, - std::span NPcommID, std::span efsmIv, - std::span ciphertext, std::span decrypted); - void PfsGenCryptoKey(std::span ekpfs, - std::span seed, - std::span dataKey, - std::span tweakKey); - void decryptPFS(std::span dataKey, - std::span tweakKey, std::span src_image, - std::span dst_image, u64 sector); - - void xtsXorBlock(CryptoPP::byte* x, const CryptoPP::byte* a, const CryptoPP::byte* b) { - for (int i = 0; i < 16; i++) { - x[i] = a[i] ^ b[i]; - } - } - - void xtsMult(std::span encryptedTweak) { - int feedback = 0; - for (int k = 0; k < encryptedTweak.size(); k++) { - const auto tmp = (encryptedTweak[k] >> 7) & 1; - encryptedTweak[k] = ((encryptedTweak[k] << 1) + feedback) & 0xFF; - feedback = tmp; - } - if (feedback != 0) { - encryptedTweak[0] ^= 0x87; - } - } -}; diff --git a/src/core/crypto/keys.h b/src/core/crypto/keys.h deleted file mode 100644 index 441082481..000000000 --- a/src/core/crypto/keys.h +++ /dev/null @@ -1,305 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once -#include - -class FakeKeyset { -public: - // Constructor - static constexpr CryptoPP::byte Exponent1[] = { - 0x6D, 0x48, 0xE0, 0x54, 0x40, 0x25, 0xC8, 0x41, 0x29, 0x52, 0x42, 0x27, 0xEB, 0xD2, 0xC7, - 0xAB, 0x6B, 0x9C, 0x27, 0x0A, 0xB4, 0x1F, 0x94, 0x4E, 0xFA, 0x42, 0x1D, 0xB7, 0xBC, 0xB9, - 0xAE, 0xBC, 0x04, 0x6F, 0x75, 0x8F, 0x10, 0x5F, 0x89, 0xAC, 0xAB, 0x9C, 0xD2, 0xFA, 0xE6, - 0xA4, 0x13, 0x83, 0x68, 0xD4, 0x56, 0x38, 0xFE, 0xE5, 0x2B, 0x78, 0x44, 0x9C, 0x34, 0xE6, - 0x5A, 0xA0, 0xBE, 0x05, 0x70, 0xAD, 0x15, 0xC3, 0x2D, 0x31, 0xAC, 0x97, 0x5D, 0x88, 0xFC, - 0xC1, 0x62, 0x3D, 0xE2, 0xED, 0x11, 0xDB, 0xB6, 0x9E, 0xFC, 0x5A, 0x5A, 0x03, 0xF6, 0xCF, - 0x08, 0xD4, 0x5D, 0x90, 0xC9, 0x2A, 0xB9, 0x9B, 0xCF, 0xC8, 0x1A, 0x65, 0xF3, 0x5B, 0xE8, - 0x7F, 0xCF, 0xA5, 0xA6, 0x4C, 0x5C, 0x2A, 0x12, 0x0F, 0x92, 0xA5, 0xE3, 0xF0, 0x17, 0x1E, - 0x9A, 0x97, 0x45, 0x86, 0xFD, 0xDB, 0x54, 0x25}; - // exponent2 = d mod (q - 1) - static constexpr CryptoPP::byte Exponent2[] = { - 0x2A, 0x51, 0xCE, 0x02, 0x44, 0x28, 0x50, 0xE8, 0x30, 0x20, 0x7C, 0x9C, 0x55, 0xBF, 0x60, - 0x39, 0xBC, 0xD1, 0xF0, 0xE7, 0x68, 0xF8, 0x08, 0x5B, 0x61, 0x1F, 0xA7, 0xBF, 0xD0, 0xE8, - 0x8B, 0xB5, 0xB1, 0xD5, 0xD9, 0x16, 0xAC, 0x75, 0x0C, 0x6D, 0xF2, 0xE0, 0xB5, 0x97, 0x75, - 0xD2, 0x68, 0x16, 0x1F, 0x00, 0x7D, 0x8B, 0x17, 0xE8, 0x78, 0x48, 0x41, 0x71, 0x2B, 0x18, - 0x96, 0x80, 0x11, 0xDB, 0x68, 0x39, 0x9C, 0xD6, 0xE0, 0x72, 0x42, 0x86, 0xF0, 0x1B, 0x16, - 0x0D, 0x3E, 0x12, 0x94, 0x3D, 0x25, 0xA8, 0xA9, 0x30, 0x9E, 0x54, 0x5A, 0xD6, 0x36, 0x6C, - 0xD6, 0x8C, 0x20, 0x62, 0x8F, 0xA1, 0x6B, 0x1F, 0x7C, 0x6D, 0xB2, 0xB1, 0xC1, 0x2E, 0xAD, - 0x36, 0x02, 0x9C, 0x3A, 0xCA, 0x2F, 0x09, 0xD2, 0x45, 0x9E, 0xEB, 0xF2, 0xBC, 0x6C, 0xAA, - 0x3B, 0x3E, 0x90, 0xBC, 0x38, 0x67, 0x35, 0x4D}; - // e - static constexpr CryptoPP::byte PublicExponent[] = {0, 1, 0, 1}; - // (InverseQ)(q) = 1 mod p - static constexpr CryptoPP::byte Coefficient[] = { - 0x0B, 0x67, 0x1C, 0x0D, 0x6C, 0x57, 0xD3, 0xE7, 0x05, 0x65, 0x94, 0x31, 0x56, 0x55, 0xFD, - 0x28, 0x08, 0xFA, 0x05, 0x8A, 0xCC, 0x55, 0x39, 0x61, 0x97, 0x63, 0xA0, 0x16, 0x27, 0x3D, - 0xED, 0xC1, 0x16, 0x40, 0x2A, 0x12, 0xEA, 0x6F, 0xD9, 0xD8, 0x58, 0x56, 0xA8, 0x56, 0x8B, - 0x0D, 0x38, 0x5E, 0x1E, 0x80, 0x3B, 0x5F, 0x40, 0x80, 0x6F, 0x62, 0x4F, 0x28, 0xA2, 0x69, - 0xF3, 0xD3, 0xF7, 0xFD, 0xB2, 0xC3, 0x52, 0x43, 0x20, 0x92, 0x9D, 0x97, 0x8D, 0xA0, 0x15, - 0x07, 0x15, 0x6E, 0xA4, 0x0D, 0x56, 0xD3, 0x37, 0x1A, 0xC4, 0x9E, 0xDF, 0x02, 0x49, 0xB8, - 0x0A, 0x84, 0x62, 0xF5, 0xFA, 0xB9, 0x3F, 0xA4, 0x09, 0x76, 0xCC, 0xAA, 0xB9, 0x9B, 0xA6, - 0x4F, 0xC1, 0x6A, 0x64, 0xCE, 0xD8, 0x77, 0xAB, 0x4B, 0xF9, 0xA0, 0xAE, 0xDA, 0xF1, 0x67, - 0x87, 0x7C, 0x98, 0x5C, 0x7E, 0xB8, 0x73, 0xF5}; - // n = p * q - static constexpr CryptoPP::byte Modulus[] = { - 0xC6, 0xCF, 0x71, 0xE7, 0xE5, 0x9A, 0xF0, 0xD1, 0x2A, 0x2C, 0x45, 0x8B, 0xF9, 0x2A, 0x0E, - 0xC1, 0x43, 0x05, 0x8B, 0xC3, 0x71, 0x17, 0x80, 0x1D, 0xCD, 0x49, 0x7D, 0xDE, 0x35, 0x9D, - 0x25, 0x9B, 0xA0, 0xD7, 0xA0, 0xF2, 0x7D, 0x6C, 0x08, 0x7E, 0xAA, 0x55, 0x02, 0x68, 0x2B, - 0x23, 0xC6, 0x44, 0xB8, 0x44, 0x18, 0xEB, 0x56, 0xCF, 0x16, 0xA2, 0x48, 0x03, 0xC9, 0xE7, - 0x4F, 0x87, 0xEB, 0x3D, 0x30, 0xC3, 0x15, 0x88, 0xBF, 0x20, 0xE7, 0x9D, 0xFF, 0x77, 0x0C, - 0xDE, 0x1D, 0x24, 0x1E, 0x63, 0xA9, 0x4F, 0x8A, 0xBF, 0x5B, 0xBE, 0x60, 0x19, 0x68, 0x33, - 0x3B, 0xFC, 0xED, 0x9F, 0x47, 0x4E, 0x5F, 0xF8, 0xEA, 0xCB, 0x3D, 0x00, 0xBD, 0x67, 0x01, - 0xF9, 0x2C, 0x6D, 0xC6, 0xAC, 0x13, 0x64, 0xE7, 0x67, 0x14, 0xF3, 0xDC, 0x52, 0x69, 0x6A, - 0xB9, 0x83, 0x2C, 0x42, 0x30, 0x13, 0x1B, 0xB2, 0xD8, 0xA5, 0x02, 0x0D, 0x79, 0xED, 0x96, - 0xB1, 0x0D, 0xF8, 0xCC, 0x0C, 0xDF, 0x81, 0x95, 0x4F, 0x03, 0x58, 0x09, 0x57, 0x0E, 0x80, - 0x69, 0x2E, 0xFE, 0xFF, 0x52, 0x77, 0xEA, 0x75, 0x28, 0xA8, 0xFB, 0xC9, 0xBE, 0xBF, 0x9F, - 0xBB, 0xB7, 0x79, 0x8E, 0x18, 0x05, 0xE1, 0x80, 0xBD, 0x50, 0x34, 0x94, 0x81, 0xD3, 0x53, - 0xC2, 0x69, 0xA2, 0xD2, 0x4C, 0xCF, 0x6C, 0xF4, 0x57, 0x2C, 0x10, 0x4A, 0x3F, 0xFB, 0x22, - 0xFD, 0x8B, 0x97, 0xE2, 0xC9, 0x5B, 0xA6, 0x2B, 0xCD, 0xD6, 0x1B, 0x6B, 0xDB, 0x68, 0x7F, - 0x4B, 0xC2, 0xA0, 0x50, 0x34, 0xC0, 0x05, 0xE5, 0x8D, 0xEF, 0x24, 0x67, 0xFF, 0x93, 0x40, - 0xCF, 0x2D, 0x62, 0xA2, 0xA0, 0x50, 0xB1, 0xF1, 0x3A, 0xA8, 0x3D, 0xFD, 0x80, 0xD1, 0xF9, - 0xB8, 0x05, 0x22, 0xAF, 0xC8, 0x35, 0x45, 0x90, 0x58, 0x8E, 0xE3, 0x3A, 0x7C, 0xBD, 0x3E, - 0x27}; - // p - static constexpr CryptoPP::byte Prime1[] = { - 0xFE, 0xF6, 0xBF, 0x1D, 0x69, 0xAB, 0x16, 0x25, 0x08, 0x47, 0x55, 0x6B, 0x86, 0xE4, 0x35, - 0x88, 0x72, 0x2A, 0xB1, 0x3D, 0xF8, 0xB6, 0x44, 0xCA, 0xB3, 0xAB, 0x19, 0xD1, 0x04, 0x24, - 0x28, 0x0A, 0x74, 0x55, 0xB8, 0x15, 0x45, 0x09, 0xCC, 0x13, 0x1C, 0xF2, 0xBA, 0x37, 0xA9, - 0x03, 0x90, 0x8F, 0x02, 0x10, 0xFF, 0x25, 0x79, 0x86, 0xCC, 0x18, 0x50, 0x9A, 0x10, 0x5F, - 0x5B, 0x4C, 0x1C, 0x4E, 0xB0, 0xA7, 0xE3, 0x59, 0xB1, 0x2D, 0xA0, 0xC6, 0xB0, 0x20, 0x2C, - 0x21, 0x33, 0x12, 0xB3, 0xAF, 0x72, 0x34, 0x83, 0xCD, 0x52, 0x2F, 0xAF, 0x0F, 0x20, 0x5A, - 0x1B, 0xC0, 0xE2, 0xA3, 0x76, 0x34, 0x0F, 0xD7, 0xFC, 0xC1, 0x41, 0xC9, 0xF9, 0x79, 0x40, - 0x17, 0x42, 0x21, 0x3E, 0x9D, 0xFD, 0xC7, 0xC1, 0x50, 0xDE, 0x44, 0x5A, 0xC9, 0x31, 0x89, - 0x6A, 0x78, 0x05, 0xBE, 0x65, 0xB4, 0xE8, 0x2D}; - // q - static constexpr CryptoPP::byte Prime2[] = { - 0xC7, 0x9E, 0x47, 0x58, 0x00, 0x7D, 0x62, 0x82, 0xB0, 0xD2, 0x22, 0x81, 0xD4, 0xA8, 0x97, - 0x1B, 0x79, 0x0C, 0x3A, 0xB0, 0xD7, 0xC9, 0x30, 0xE3, 0xC3, 0x53, 0x8E, 0x57, 0xEF, 0xF0, - 0x9B, 0x9F, 0xB3, 0x90, 0x52, 0xC6, 0x94, 0x22, 0x36, 0xAA, 0xE6, 0x4A, 0x5F, 0x72, 0x1D, - 0x70, 0xE8, 0x76, 0x58, 0xC8, 0xB2, 0x91, 0xCE, 0x9C, 0xC3, 0xE9, 0x09, 0x7F, 0x2E, 0x47, - 0x97, 0xCC, 0x90, 0x39, 0x15, 0x35, 0x31, 0xDE, 0x1F, 0x0C, 0x8C, 0x0D, 0xC1, 0xC2, 0x92, - 0xBE, 0x97, 0xBF, 0x2F, 0x91, 0xA1, 0x8C, 0x7D, 0x50, 0xA8, 0x21, 0x2F, 0xD7, 0xA2, 0x9A, - 0x7E, 0xB5, 0xA7, 0x2A, 0x90, 0x02, 0xD9, 0xF3, 0x3D, 0xD1, 0xEB, 0xB8, 0xE0, 0x5A, 0x79, - 0x9E, 0x7D, 0x8D, 0xCA, 0x18, 0x6D, 0xBD, 0x9E, 0xA1, 0x80, 0x28, 0x6B, 0x2A, 0xFE, 0x51, - 0x24, 0x9B, 0x6F, 0x4D, 0x84, 0x77, 0x80, 0x23}; - static constexpr CryptoPP::byte PrivateExponent[] = { - 0x7F, 0x76, 0xCD, 0x0E, 0xE2, 0xD4, 0xDE, 0x05, 0x1C, 0xC6, 0xD9, 0xA8, 0x0E, 0x8D, 0xFA, - 0x7B, 0xCA, 0x1E, 0xAA, 0x27, 0x1A, 0x40, 0xF8, 0xF1, 0x22, 0x87, 0x35, 0xDD, 0xDB, 0xFD, - 0xEE, 0xF8, 0xC2, 0xBC, 0xBD, 0x01, 0xFB, 0x8B, 0xE2, 0x3E, 0x63, 0xB2, 0xB1, 0x22, 0x5C, - 0x56, 0x49, 0x6E, 0x11, 0xBE, 0x07, 0x44, 0x0B, 0x9A, 0x26, 0x66, 0xD1, 0x49, 0x2C, 0x8F, - 0xD3, 0x1B, 0xCF, 0xA4, 0xA1, 0xB8, 0xD1, 0xFB, 0xA4, 0x9E, 0xD2, 0x21, 0x28, 0x83, 0x09, - 0x8A, 0xF6, 0xA0, 0x0B, 0xA3, 0xD6, 0x0F, 0x9B, 0x63, 0x68, 0xCC, 0xBC, 0x0C, 0x4E, 0x14, - 0x5B, 0x27, 0xA4, 0xA9, 0xF4, 0x2B, 0xB9, 0xB8, 0x7B, 0xC0, 0xE6, 0x51, 0xAD, 0x1D, 0x77, - 0xD4, 0x6B, 0xB9, 0xCE, 0x20, 0xD1, 0x26, 0x66, 0x7E, 0x5E, 0x9E, 0xA2, 0xE9, 0x6B, 0x90, - 0xF3, 0x73, 0xB8, 0x52, 0x8F, 0x44, 0x11, 0x03, 0x0C, 0x13, 0x97, 0x39, 0x3D, 0x13, 0x22, - 0x58, 0xD5, 0x43, 0x82, 0x49, 0xDA, 0x6E, 0x7C, 0xA1, 0xC5, 0x8C, 0xA5, 0xB0, 0x09, 0xE0, - 0xCE, 0x3D, 0xDF, 0xF4, 0x9D, 0x3C, 0x97, 0x15, 0xE2, 0x6A, 0xC7, 0x2B, 0x3C, 0x50, 0x93, - 0x23, 0xDB, 0xBA, 0x4A, 0x22, 0x66, 0x44, 0xAC, 0x78, 0xBB, 0x0E, 0x1A, 0x27, 0x43, 0xB5, - 0x71, 0x67, 0xAF, 0xF4, 0xAB, 0x48, 0x46, 0x93, 0x73, 0xD0, 0x42, 0xAB, 0x93, 0x63, 0xE5, - 0x6C, 0x9A, 0xDE, 0x50, 0x24, 0xC0, 0x23, 0x7D, 0x99, 0x79, 0x3F, 0x22, 0x07, 0xE0, 0xC1, - 0x48, 0x56, 0x1B, 0xDF, 0x83, 0x09, 0x12, 0xB4, 0x2D, 0x45, 0x6B, 0xC9, 0xC0, 0x68, 0x85, - 0x99, 0x90, 0x79, 0x96, 0x1A, 0xD7, 0xF5, 0x4D, 0x1F, 0x37, 0x83, 0x40, 0x4A, 0xEC, 0x39, - 0x37, 0xA6, 0x80, 0x92, 0x7D, 0xC5, 0x80, 0xC7, 0xD6, 0x6F, 0xFE, 0x8A, 0x79, 0x89, 0xC6, - 0xB1}; -}; - -class DebugRifKeyset { -public: - // std::uint8_t* PrivateExponent; - static constexpr CryptoPP::byte Exponent1[] = { - 0xCD, 0x9A, 0x61, 0xB0, 0xB8, 0xD5, 0xB4, 0xE4, 0xE4, 0xF6, 0xAB, 0xF7, 0x27, 0xB7, 0x56, - 0x59, 0x6B, 0xB9, 0x11, 0xE7, 0xF4, 0x83, 0xAF, 0xB9, 0x73, 0x99, 0x7F, 0x49, 0xA2, 0x9C, - 0xF0, 0xB5, 0x6D, 0x37, 0x82, 0x14, 0x15, 0xF1, 0x04, 0x8A, 0xD4, 0x8E, 0xEB, 0x2E, 0x1F, - 0xE2, 0x81, 0xA9, 0x62, 0x6E, 0xB1, 0x68, 0x75, 0x62, 0xF3, 0x0F, 0xFE, 0xD4, 0x91, 0x87, - 0x98, 0x78, 0xBF, 0x26, 0xB5, 0x07, 0x58, 0xD0, 0xEE, 0x3F, 0x21, 0xE8, 0xC8, 0x0F, 0x5F, - 0xFA, 0x1C, 0x64, 0x74, 0x49, 0x52, 0xEB, 0xE7, 0xEE, 0xDE, 0xBA, 0x23, 0x26, 0x4A, 0xF6, - 0x9C, 0x1A, 0x09, 0x3F, 0xB9, 0x0B, 0x36, 0x26, 0x1A, 0xBE, 0xA9, 0x76, 0xE6, 0xF2, 0x69, - 0xDE, 0xFF, 0xAF, 0xCC, 0x0C, 0x9A, 0x66, 0x03, 0x86, 0x0A, 0x1F, 0x49, 0xA4, 0x10, 0xB6, - 0xBC, 0xC3, 0x7C, 0x88, 0xE8, 0xCE, 0x4B, 0xD9}; - // exponent2 = d mod (q - 1) - static constexpr CryptoPP::byte Exponent2[] = { - 0xB3, 0x73, 0xA3, 0x59, 0xE6, 0x97, 0xC0, 0xAB, 0x3B, 0x68, 0xFC, 0x39, 0xAC, 0xDB, 0x44, - 0xB1, 0xB4, 0x9E, 0x35, 0x4D, 0xBE, 0xC5, 0x36, 0x69, 0x6C, 0x3D, 0xC5, 0xFC, 0xFE, 0x4B, - 0x2F, 0xDC, 0x86, 0x80, 0x46, 0x96, 0x40, 0x1A, 0x0D, 0x6E, 0xFA, 0x8C, 0xE0, 0x47, 0x91, - 0xAC, 0xAD, 0x95, 0x2B, 0x8E, 0x1F, 0xF2, 0x0A, 0x45, 0xF8, 0x29, 0x95, 0x70, 0xC6, 0x88, - 0x5F, 0x71, 0x03, 0x99, 0x79, 0xBC, 0x84, 0x71, 0xBD, 0xE8, 0x84, 0x8C, 0x0E, 0xD4, 0x7B, - 0x30, 0x74, 0x57, 0x1A, 0x95, 0xE7, 0x90, 0x19, 0x8D, 0xAD, 0x8B, 0x4C, 0x4E, 0xC3, 0xE7, - 0x6B, 0x23, 0x86, 0x01, 0xEE, 0x9B, 0xE0, 0x2F, 0x15, 0xA2, 0x2C, 0x4C, 0x39, 0xD3, 0xDF, - 0x9C, 0x39, 0x01, 0xF1, 0x8C, 0x44, 0x4A, 0x15, 0x44, 0xDC, 0x51, 0xF7, 0x22, 0xD7, 0x7F, - 0x41, 0x7F, 0x68, 0xFA, 0xEE, 0x56, 0xE8, 0x05}; - // e - static constexpr CryptoPP::byte PublicExponent[] = {0x00, 0x01, 0x00, 0x01}; - // (InverseQ)(q) = 1 mod p - static constexpr CryptoPP::byte Coefficient[] = { - 0xC0, 0x32, 0x43, 0xD3, 0x8C, 0x3D, 0xB4, 0xD2, 0x48, 0x8C, 0x42, 0x41, 0x24, 0x94, 0x6C, - 0x80, 0xC9, 0xC1, 0x79, 0x36, 0x7F, 0xAC, 0xC3, 0xFF, 0x6A, 0x25, 0xEB, 0x2C, 0xFB, 0xD4, - 0x2B, 0xA0, 0xEB, 0xFE, 0x25, 0xE9, 0xC6, 0x77, 0xCE, 0xFE, 0x2D, 0x23, 0xFE, 0xD0, 0xF4, - 0x0F, 0xD9, 0x7E, 0xD5, 0xA5, 0x7D, 0x1F, 0xC0, 0xE8, 0xE8, 0xEC, 0x80, 0x5B, 0xC7, 0xFD, - 0xE2, 0xBD, 0x94, 0xA6, 0x2B, 0xDD, 0x6A, 0x60, 0x45, 0x54, 0xAB, 0xCA, 0x42, 0x9C, 0x6A, - 0x6C, 0xBF, 0x3C, 0x84, 0xF9, 0xA5, 0x0E, 0x63, 0x0C, 0x51, 0x58, 0x62, 0x6D, 0x5A, 0xB7, - 0x3C, 0x3F, 0x49, 0x1A, 0xD0, 0x93, 0xB8, 0x4F, 0x1A, 0x6C, 0x5F, 0xC5, 0xE5, 0xA9, 0x75, - 0xD4, 0x86, 0x9E, 0xDF, 0x87, 0x0F, 0x27, 0xB0, 0x26, 0x78, 0x4E, 0xFB, 0xC1, 0x8A, 0x4A, - 0x24, 0x3F, 0x7F, 0x8F, 0x9A, 0x12, 0x51, 0xCB}; - // n = p * q - static constexpr CryptoPP::byte Modulus[] = { - 0xC2, 0xD2, 0x44, 0xBC, 0xDD, 0x84, 0x3F, 0xD9, 0xC5, 0x22, 0xAF, 0xF7, 0xFC, 0x88, 0x8A, - 0x33, 0x80, 0xED, 0x8E, 0xE2, 0xCC, 0x81, 0xF7, 0xEC, 0xF8, 0x1C, 0x79, 0xBF, 0x02, 0xBB, - 0x12, 0x8E, 0x61, 0x68, 0x29, 0x1B, 0x15, 0xB6, 0x5E, 0xC6, 0xF8, 0xBF, 0x5A, 0xE0, 0x3B, - 0x6A, 0x6C, 0xD9, 0xD6, 0xF5, 0x75, 0xAB, 0xA0, 0x6F, 0x34, 0x81, 0x34, 0x9A, 0x5B, 0xAD, - 0xED, 0x31, 0xE3, 0xC6, 0xEA, 0x1A, 0xD1, 0x13, 0x22, 0xBB, 0xB3, 0xDA, 0xB3, 0xB2, 0x53, - 0xBD, 0x45, 0x79, 0x87, 0xAD, 0x0A, 0x01, 0x72, 0x18, 0x10, 0x29, 0x49, 0xF4, 0x41, 0x7F, - 0xD6, 0x47, 0x0C, 0x72, 0x92, 0x9E, 0xE9, 0xBB, 0x95, 0xA9, 0x5D, 0x79, 0xEB, 0xE4, 0x30, - 0x76, 0x90, 0x45, 0x4B, 0x9D, 0x9C, 0xCF, 0x92, 0x03, 0x60, 0x8C, 0x4B, 0x6C, 0xB3, 0x7A, - 0x3A, 0x05, 0x39, 0xA0, 0x66, 0xA9, 0x35, 0xCF, 0xB9, 0xFA, 0xAD, 0x9C, 0xAB, 0xEB, 0xE4, - 0x6A, 0x8C, 0xE9, 0x3B, 0xCC, 0x72, 0x12, 0x62, 0x63, 0xBD, 0x80, 0xC4, 0xEE, 0x37, 0x2B, - 0x32, 0x03, 0xA3, 0x09, 0xF7, 0xA0, 0x61, 0x57, 0xAD, 0x0D, 0xCF, 0x15, 0x98, 0x9E, 0x4E, - 0x49, 0xF8, 0xB5, 0xA3, 0x5C, 0x27, 0xEE, 0x45, 0x04, 0xEA, 0xE4, 0x4B, 0xBC, 0x8F, 0x87, - 0xED, 0x19, 0x1E, 0x46, 0x75, 0x63, 0xC4, 0x5B, 0xD5, 0xBC, 0x09, 0x2F, 0x02, 0x73, 0x19, - 0x3C, 0x58, 0x55, 0x49, 0x66, 0x4C, 0x11, 0xEC, 0x0F, 0x09, 0xFA, 0xA5, 0x56, 0x0A, 0x5A, - 0x63, 0x56, 0xAD, 0xA0, 0x0D, 0x86, 0x08, 0xC1, 0xE6, 0xB6, 0x13, 0x22, 0x49, 0x2F, 0x7C, - 0xDB, 0x4C, 0x56, 0x97, 0x0E, 0xC2, 0xD9, 0x2E, 0x87, 0xBC, 0x0E, 0x67, 0xC0, 0x1B, 0x58, - 0xBC, 0x64, 0x2B, 0xC2, 0x6E, 0xE2, 0x93, 0x2E, 0xB5, 0x6B, 0x70, 0xA4, 0x42, 0x9F, 0x64, - 0xC1}; - // p - static constexpr CryptoPP::byte Prime1[] = { - 0xE5, 0x62, 0xE1, 0x7F, 0x9F, 0x86, 0x08, 0xE2, 0x61, 0xD3, 0xD0, 0x42, 0xE2, 0xC4, 0xB6, - 0xA8, 0x51, 0x09, 0x19, 0x14, 0xA4, 0x3A, 0x11, 0x4C, 0x33, 0xA5, 0x9C, 0x01, 0x5E, 0x34, - 0xB6, 0x3F, 0x02, 0x1A, 0xCA, 0x47, 0xF1, 0x4F, 0x3B, 0x35, 0x2A, 0x07, 0x20, 0xEC, 0xD8, - 0xC1, 0x15, 0xD9, 0xCA, 0x03, 0x4F, 0xB8, 0xE8, 0x09, 0x73, 0x3F, 0x85, 0xB7, 0x41, 0xD5, - 0x51, 0x3E, 0x7B, 0xE3, 0x53, 0x2B, 0x48, 0x8B, 0x8E, 0xCB, 0xBA, 0xF7, 0xE0, 0x60, 0xF5, - 0x35, 0x0E, 0x6F, 0xB0, 0xD9, 0x2A, 0x99, 0xD0, 0xFF, 0x60, 0x14, 0xED, 0x40, 0xEA, 0xF8, - 0xD7, 0x0B, 0xC3, 0x8D, 0x8C, 0xE8, 0x81, 0xB3, 0x75, 0x93, 0x15, 0xB3, 0x7D, 0xF6, 0x39, - 0x60, 0x1A, 0x00, 0xE7, 0xC3, 0x27, 0xAD, 0xA4, 0x33, 0xD5, 0x3E, 0xA4, 0x35, 0x48, 0x6F, - 0x22, 0xEF, 0x5D, 0xDD, 0x7D, 0x7B, 0x61, 0x05}; - // q - static constexpr CryptoPP::byte Prime2[] = { - 0xD9, 0x6C, 0xC2, 0x0C, 0xF7, 0xAE, 0xD1, 0xF3, 0x3B, 0x3B, 0x49, 0x1E, 0x9F, 0x12, 0x9C, - 0xA1, 0x78, 0x1F, 0x35, 0x1D, 0x98, 0x26, 0x13, 0x71, 0xF9, 0x09, 0xFD, 0xF0, 0xAD, 0x38, - 0x55, 0xB7, 0xEE, 0x61, 0x04, 0x72, 0x51, 0x87, 0x2E, 0x05, 0x84, 0xB1, 0x1D, 0x0C, 0x0D, - 0xDB, 0xD4, 0x25, 0x3E, 0x26, 0xED, 0xEA, 0xB8, 0xF7, 0x49, 0xFE, 0xA2, 0x94, 0xE6, 0xF2, - 0x08, 0x92, 0xA7, 0x85, 0xF5, 0x30, 0xB9, 0x84, 0x22, 0xBF, 0xCA, 0xF0, 0x5F, 0xCB, 0x31, - 0x20, 0x34, 0x49, 0x16, 0x76, 0x34, 0xCC, 0x7A, 0xCB, 0x96, 0xFE, 0x78, 0x7A, 0x41, 0xFE, - 0x9A, 0xA2, 0x23, 0xF7, 0x68, 0x80, 0xD6, 0xCE, 0x4A, 0x78, 0xA5, 0xB7, 0x05, 0x77, 0x81, - 0x1F, 0xDE, 0x5E, 0xA8, 0x6E, 0x3E, 0x87, 0xEC, 0x44, 0xD2, 0x69, 0xC6, 0x54, 0x91, 0x6B, - 0x5E, 0x13, 0x8A, 0x03, 0x87, 0x05, 0x31, 0x8D}; - static constexpr CryptoPP::byte PrivateExponent[] = { - 0x01, 0x61, 0xAD, 0xD8, 0x9C, 0x06, 0x89, 0xD0, 0x60, 0xC8, 0x41, 0xF0, 0xB3, 0x83, 0x01, - 0x5D, 0xE3, 0xA2, 0x6B, 0xA2, 0xBA, 0x9A, 0x0A, 0x58, 0xCD, 0x1A, 0xA0, 0x97, 0x64, 0xEC, - 0xD0, 0x31, 0x1F, 0xCA, 0x36, 0x0E, 0x69, 0xDD, 0x40, 0xF7, 0x4E, 0xC0, 0xC6, 0xA3, 0x73, - 0xF0, 0x69, 0x84, 0xB2, 0xF4, 0x4B, 0x29, 0x14, 0x2A, 0x6D, 0xB8, 0x23, 0xD8, 0x1B, 0x61, - 0xD4, 0x9E, 0x87, 0xB3, 0xBB, 0xA9, 0xC4, 0x85, 0x4A, 0xF8, 0x03, 0x4A, 0xBF, 0xFE, 0xF9, - 0xFE, 0x8B, 0xDD, 0x54, 0x83, 0xBA, 0xE0, 0x2F, 0x3F, 0xB1, 0xEF, 0xA5, 0x05, 0x5D, 0x28, - 0x8B, 0xAB, 0xB5, 0xD0, 0x23, 0x2F, 0x8A, 0xCF, 0x48, 0x7C, 0xAA, 0xBB, 0xC8, 0x5B, 0x36, - 0x27, 0xC5, 0x16, 0xA4, 0xB6, 0x61, 0xAC, 0x0C, 0x28, 0x47, 0x79, 0x3F, 0x38, 0xAE, 0x5E, - 0x25, 0xC6, 0xAF, 0x35, 0xAE, 0xBC, 0xB0, 0xF3, 0xBC, 0xBD, 0xFD, 0xA4, 0x87, 0x0D, 0x14, - 0x3D, 0x90, 0xE4, 0xDE, 0x5D, 0x1D, 0x46, 0x81, 0xF1, 0x28, 0x6D, 0x2F, 0x2C, 0x5E, 0x97, - 0x2D, 0x89, 0x2A, 0x51, 0x72, 0x3C, 0x20, 0x02, 0x59, 0xB1, 0x98, 0x93, 0x05, 0x1E, 0x3F, - 0xA1, 0x8A, 0x69, 0x30, 0x0E, 0x70, 0x84, 0x8B, 0xAE, 0x97, 0xA1, 0x08, 0x95, 0x63, 0x4C, - 0xC7, 0xE8, 0x5D, 0x59, 0xCA, 0x78, 0x2A, 0x23, 0x87, 0xAC, 0x6F, 0x04, 0x33, 0xB1, 0x61, - 0xB9, 0xF0, 0x95, 0xDA, 0x33, 0xCC, 0xE0, 0x4C, 0x82, 0x68, 0x82, 0x14, 0x51, 0xBE, 0x49, - 0x1C, 0x58, 0xA2, 0x8B, 0x05, 0x4E, 0x98, 0x37, 0xEB, 0x94, 0x0B, 0x01, 0x22, 0xDC, 0xB3, - 0x19, 0xCA, 0x77, 0xA6, 0x6E, 0x97, 0xFF, 0x8A, 0x53, 0x5A, 0xC5, 0x24, 0xE4, 0xAF, 0x6E, - 0xA8, 0x2B, 0x53, 0xA4, 0xBE, 0x96, 0xA5, 0x7B, 0xCE, 0x22, 0x56, 0xA3, 0xF1, 0xCF, 0x14, - 0xA5}; -}; - -class PkgDerivedKey3Keyset { -public: - // std::uint8_t* PrivateExponent; - static constexpr CryptoPP::byte Exponent1[] = { - 0x52, 0xCC, 0x2D, 0xA0, 0x9C, 0x9E, 0x75, 0xE7, 0x28, 0xEE, 0x3D, 0xDE, 0xE3, 0x45, 0xD1, - 0x4F, 0x94, 0x1C, 0xCC, 0xC8, 0x87, 0x29, 0x45, 0x3B, 0x8D, 0x6E, 0xAB, 0x6E, 0x2A, 0xA7, - 0xC7, 0x15, 0x43, 0xA3, 0x04, 0x8F, 0x90, 0x5F, 0xEB, 0xF3, 0x38, 0x4A, 0x77, 0xFA, 0x36, - 0xB7, 0x15, 0x76, 0xB6, 0x01, 0x1A, 0x8E, 0x25, 0x87, 0x82, 0xF1, 0x55, 0xD8, 0xC6, 0x43, - 0x2A, 0xC0, 0xE5, 0x98, 0xC9, 0x32, 0xD1, 0x94, 0x6F, 0xD9, 0x01, 0xBA, 0x06, 0x81, 0xE0, - 0x6D, 0x88, 0xF2, 0x24, 0x2A, 0x25, 0x01, 0x64, 0x5C, 0xBF, 0xF2, 0xD9, 0x99, 0x67, 0x3E, - 0xF6, 0x72, 0xEE, 0xE4, 0xE2, 0x33, 0x5C, 0xF8, 0x00, 0x40, 0xE3, 0x2A, 0x9A, 0xF4, 0x3D, - 0x22, 0x86, 0x44, 0x3C, 0xFB, 0x0A, 0xA5, 0x7C, 0x3F, 0xCC, 0xF5, 0xF1, 0x16, 0xC4, 0xAC, - 0x88, 0xB4, 0xDE, 0x62, 0x94, 0x92, 0x6A, 0x13}; - // exponent2 = d mod (q - 1) - static constexpr CryptoPP::byte Exponent2[] = { - 0x7C, 0x9D, 0xAD, 0x39, 0xE0, 0xD5, 0x60, 0x14, 0x94, 0x48, 0x19, 0x7F, 0x88, 0x95, 0xD5, - 0x8B, 0x80, 0xAD, 0x85, 0x8A, 0x4B, 0x77, 0x37, 0x85, 0xD0, 0x77, 0xBB, 0xBF, 0x89, 0x71, - 0x4A, 0x72, 0xCB, 0x72, 0x68, 0x38, 0xEC, 0x02, 0xC6, 0x7D, 0xC6, 0x44, 0x06, 0x33, 0x51, - 0x1C, 0xC0, 0xFF, 0x95, 0x8F, 0x0D, 0x75, 0xDC, 0x25, 0xBB, 0x0B, 0x73, 0x91, 0xA9, 0x6D, - 0x42, 0xD8, 0x03, 0xB7, 0x68, 0xD4, 0x1E, 0x75, 0x62, 0xA3, 0x70, 0x35, 0x79, 0x78, 0x00, - 0xC8, 0xF5, 0xEF, 0x15, 0xB9, 0xFC, 0x4E, 0x47, 0x5A, 0xC8, 0x70, 0x70, 0x5B, 0x52, 0x98, - 0xC0, 0xC2, 0x58, 0x4A, 0x70, 0x96, 0xCC, 0xB8, 0x10, 0xE1, 0x2F, 0x78, 0x8B, 0x2B, 0xA1, - 0x7F, 0xF9, 0xAC, 0xDE, 0xF0, 0xBB, 0x2B, 0xE2, 0x66, 0xE3, 0x22, 0x92, 0x31, 0x21, 0x57, - 0x92, 0xC4, 0xB8, 0xF2, 0x3E, 0x76, 0x20, 0x37}; - // e - static constexpr CryptoPP::byte PublicExponent[] = {0, 1, 0, 1}; - // (InverseQ)(q) = 1 mod p - static constexpr CryptoPP::byte Coefficient[] = { - 0x45, 0x97, 0x55, 0xD4, 0x22, 0x08, 0x5E, 0xF3, 0x5C, 0xB4, 0x05, 0x7A, 0xFD, 0xAA, 0x42, - 0x42, 0xAD, 0x9A, 0x8C, 0xA0, 0x6C, 0xBB, 0x1D, 0x68, 0x54, 0x54, 0x6E, 0x3E, 0x32, 0xE3, - 0x53, 0x73, 0x76, 0xF1, 0x3E, 0x01, 0xEA, 0xD3, 0xCF, 0xEB, 0xEB, 0x23, 0x3E, 0xC0, 0xBE, - 0xCE, 0xEC, 0x2C, 0x89, 0x5F, 0xA8, 0x27, 0x3A, 0x4C, 0xB7, 0xE6, 0x74, 0xBC, 0x45, 0x4C, - 0x26, 0xC8, 0x25, 0xFF, 0x34, 0x63, 0x25, 0x37, 0xE1, 0x48, 0x10, 0xC1, 0x93, 0xA6, 0xAF, - 0xEB, 0xBA, 0xE3, 0xA2, 0xF1, 0x3D, 0xEF, 0x63, 0xD8, 0xF4, 0xFD, 0xD3, 0xEE, 0xE2, 0x5D, - 0xE9, 0x33, 0xCC, 0xAD, 0xBA, 0x75, 0x5C, 0x85, 0xAF, 0xCE, 0xA9, 0x3D, 0xD1, 0xA2, 0x17, - 0xF3, 0xF6, 0x98, 0xB3, 0x50, 0x8E, 0x5E, 0xF6, 0xEB, 0x02, 0x8E, 0xA1, 0x62, 0xA7, 0xD6, - 0x2C, 0xEC, 0x91, 0xFF, 0x15, 0x40, 0xD2, 0xE3}; - // n = p * q - static constexpr CryptoPP::byte Modulus[] = { - 0xd2, 0x12, 0xfc, 0x33, 0x5f, 0x6d, 0xdb, 0x83, 0x16, 0x09, 0x62, 0x8b, 0x03, 0x56, 0x27, - 0x37, 0x82, 0xd4, 0x77, 0x85, 0x35, 0x29, 0x39, 0x2d, 0x52, 0x6b, 0x8c, 0x4c, 0x8c, 0xfb, - 0x06, 0xc1, 0x84, 0x5b, 0xe7, 0xd4, 0xf7, 0xbc, 0xd2, 0x4e, 0x62, 0x45, 0xcd, 0x2a, 0xbb, - 0xd7, 0x77, 0x76, 0x45, 0x36, 0x55, 0x27, 0x3f, 0xb3, 0xf5, 0xf9, 0x8e, 0xda, 0x4b, 0xef, - 0xaa, 0x59, 0xae, 0xb3, 0x9b, 0xea, 0x54, 0x98, 0xd2, 0x06, 0x32, 0x6a, 0x58, 0x31, 0x2a, - 0xe0, 0xd4, 0x4f, 0x90, 0xb5, 0x0a, 0x7d, 0xec, 0xf4, 0x3a, 0x9c, 0x52, 0x67, 0x2d, 0x99, - 0x31, 0x8e, 0x0c, 0x43, 0xe6, 0x82, 0xfe, 0x07, 0x46, 0xe1, 0x2e, 0x50, 0xd4, 0x1f, 0x2d, - 0x2f, 0x7e, 0xd9, 0x08, 0xba, 0x06, 0xb3, 0xbf, 0x2e, 0x20, 0x3f, 0x4e, 0x3f, 0xfe, 0x44, - 0xff, 0xaa, 0x50, 0x43, 0x57, 0x91, 0x69, 0x94, 0x49, 0x15, 0x82, 0x82, 0xe4, 0x0f, 0x4c, - 0x8d, 0x9d, 0x2c, 0xc9, 0x5b, 0x1d, 0x64, 0xbf, 0x88, 0x8b, 0xd4, 0xc5, 0x94, 0xe7, 0x65, - 0x47, 0x84, 0x1e, 0xe5, 0x79, 0x10, 0xfb, 0x98, 0x93, 0x47, 0xb9, 0x7d, 0x85, 0x12, 0xa6, - 0x40, 0x98, 0x2c, 0xf7, 0x92, 0xbc, 0x95, 0x19, 0x32, 0xed, 0xe8, 0x90, 0x56, 0x0d, 0x65, - 0xc1, 0xaa, 0x78, 0xc6, 0x2e, 0x54, 0xfd, 0x5f, 0x54, 0xa1, 0xf6, 0x7e, 0xe5, 0xe0, 0x5f, - 0x61, 0xc1, 0x20, 0xb4, 0xb9, 0xb4, 0x33, 0x08, 0x70, 0xe4, 0xdf, 0x89, 0x56, 0xed, 0x01, - 0x29, 0x46, 0x77, 0x5f, 0x8c, 0xb8, 0xa9, 0xf5, 0x1e, 0x2e, 0xb3, 0xb9, 0xbf, 0xe0, 0x09, - 0xb7, 0x8d, 0x28, 0xd4, 0xa6, 0xc3, 0xb8, 0x1e, 0x1f, 0x07, 0xeb, 0xb4, 0x12, 0x0b, 0x95, - 0xb8, 0x85, 0x30, 0xfd, 0xdc, 0x39, 0x13, 0xd0, 0x7c, 0xdc, 0x8f, 0xed, 0xf9, 0xc9, 0xa3, - 0xc1}; - // p - static constexpr CryptoPP::byte Prime1[] = { - 0xF9, 0x67, 0xAD, 0x99, 0x12, 0x31, 0x0C, 0x56, 0xA2, 0x2E, 0x16, 0x1C, 0x46, 0xB3, 0x4D, - 0x5B, 0x43, 0xBE, 0x42, 0xA2, 0xF6, 0x86, 0x96, 0x80, 0x42, 0xC3, 0xC7, 0x3F, 0xC3, 0x42, - 0xF5, 0x87, 0x49, 0x33, 0x9F, 0x07, 0x5D, 0x6E, 0x2C, 0x04, 0xFD, 0xE3, 0xE1, 0xB2, 0xAE, - 0x0A, 0x0C, 0xF0, 0xC7, 0xA6, 0x1C, 0xA1, 0x63, 0x50, 0xC8, 0x09, 0x9C, 0x51, 0x24, 0x52, - 0x6C, 0x5E, 0x5E, 0xBD, 0x1E, 0x27, 0x06, 0xBB, 0xBC, 0x9E, 0x94, 0xE1, 0x35, 0xD4, 0x6D, - 0xB3, 0xCB, 0x3C, 0x68, 0xDD, 0x68, 0xB3, 0xFE, 0x6C, 0xCB, 0x8D, 0x82, 0x20, 0x76, 0x23, - 0x63, 0xB7, 0xE9, 0x68, 0x10, 0x01, 0x4E, 0xDC, 0xBA, 0x27, 0x5D, 0x01, 0xC1, 0x2D, 0x80, - 0x5E, 0x2B, 0xAF, 0x82, 0x6B, 0xD8, 0x84, 0xB6, 0x10, 0x52, 0x86, 0xA7, 0x89, 0x8E, 0xAE, - 0x9A, 0xE2, 0x89, 0xC6, 0xF7, 0xD5, 0x87, 0xFB}; - // q - static constexpr CryptoPP::byte Prime2[] = { - 0xD7, 0xA1, 0x0F, 0x9A, 0x8B, 0xF2, 0xC9, 0x11, 0x95, 0x32, 0x9A, 0x8C, 0xF0, 0xD9, 0x40, - 0x47, 0xF5, 0x68, 0xA0, 0x0D, 0xBD, 0xC1, 0xFC, 0x43, 0x2F, 0x65, 0xF9, 0xC3, 0x61, 0x0F, - 0x25, 0x77, 0x54, 0xAD, 0xD7, 0x58, 0xAC, 0x84, 0x40, 0x60, 0x8D, 0x3F, 0xF3, 0x65, 0x89, - 0x75, 0xB5, 0xC6, 0x2C, 0x51, 0x1A, 0x2F, 0x1F, 0x22, 0xE4, 0x43, 0x11, 0x54, 0xBE, 0xC9, - 0xB4, 0xC7, 0xB5, 0x1B, 0x05, 0x0B, 0xBC, 0x56, 0x9A, 0xCD, 0x4A, 0xD9, 0x73, 0x68, 0x5E, - 0x5C, 0xFB, 0x92, 0xB7, 0x8B, 0x0D, 0xFF, 0xF5, 0x07, 0xCA, 0xB4, 0xC8, 0x9B, 0x96, 0x3C, - 0x07, 0x9E, 0x3E, 0x6B, 0x2A, 0x11, 0xF2, 0x8A, 0xB1, 0x8A, 0xD7, 0x2E, 0x1B, 0xA5, 0x53, - 0x24, 0x06, 0xED, 0x50, 0xB8, 0x90, 0x67, 0xB1, 0xE2, 0x41, 0xC6, 0x92, 0x01, 0xEE, 0x10, - 0xF0, 0x61, 0xBB, 0xFB, 0xB2, 0x7D, 0x4A, 0x73}; - static constexpr CryptoPP::byte PrivateExponent[] = { - 0x32, 0xD9, 0x03, 0x90, 0x8F, 0xBD, 0xB0, 0x8F, 0x57, 0x2B, 0x28, 0x5E, 0x0B, 0x8D, 0xB3, - 0xEA, 0x5C, 0xD1, 0x7E, 0xA8, 0x90, 0x88, 0x8C, 0xDD, 0x6A, 0x80, 0xBB, 0xB1, 0xDF, 0xC1, - 0xF7, 0x0D, 0xAA, 0x32, 0xF0, 0xB7, 0x7C, 0xCB, 0x88, 0x80, 0x0E, 0x8B, 0x64, 0xB0, 0xBE, - 0x4C, 0xD6, 0x0E, 0x9B, 0x8C, 0x1E, 0x2A, 0x64, 0xE1, 0xF3, 0x5C, 0xD7, 0x76, 0x01, 0x41, - 0x5E, 0x93, 0x5C, 0x94, 0xFE, 0xDD, 0x46, 0x62, 0xC3, 0x1B, 0x5A, 0xE2, 0xA0, 0xBC, 0x2D, - 0xEB, 0xC3, 0x98, 0x0A, 0xA7, 0xB7, 0x85, 0x69, 0x70, 0x68, 0x2B, 0x64, 0x4A, 0xB3, 0x1F, - 0xCC, 0x7D, 0xDC, 0x7C, 0x26, 0xF4, 0x77, 0xF6, 0x5C, 0xF2, 0xAE, 0x5A, 0x44, 0x2D, 0xD3, - 0xAB, 0x16, 0x62, 0x04, 0x19, 0xBA, 0xFB, 0x90, 0xFF, 0xE2, 0x30, 0x50, 0x89, 0x6E, 0xCB, - 0x56, 0xB2, 0xEB, 0xC0, 0x91, 0x16, 0x92, 0x5E, 0x30, 0x8E, 0xAE, 0xC7, 0x94, 0x5D, 0xFD, - 0x35, 0xE1, 0x20, 0xF8, 0xAD, 0x3E, 0xBC, 0x08, 0xBF, 0xC0, 0x36, 0x74, 0x9F, 0xD5, 0xBB, - 0x52, 0x08, 0xFD, 0x06, 0x66, 0xF3, 0x7A, 0xB3, 0x04, 0xF4, 0x75, 0x29, 0x5D, 0xE9, 0x5F, - 0xAA, 0x10, 0x30, 0xB2, 0x0F, 0x5A, 0x1A, 0xC1, 0x2A, 0xB3, 0xFE, 0xCB, 0x21, 0xAD, 0x80, - 0xEC, 0x8F, 0x20, 0x09, 0x1C, 0xDB, 0xC5, 0x58, 0x94, 0xC2, 0x9C, 0xC6, 0xCE, 0x82, 0x65, - 0x3E, 0x57, 0x90, 0xBC, 0xA9, 0x8B, 0x06, 0xB4, 0xF0, 0x72, 0xF6, 0x77, 0xDF, 0x98, 0x64, - 0xF1, 0xEC, 0xFE, 0x37, 0x2D, 0xBC, 0xAE, 0x8C, 0x08, 0x81, 0x1F, 0xC3, 0xC9, 0x89, 0x1A, - 0xC7, 0x42, 0x82, 0x4B, 0x2E, 0xDC, 0x8E, 0x8D, 0x73, 0xCE, 0xB1, 0xCC, 0x01, 0xD9, 0x08, - 0x70, 0x87, 0x3C, 0x44, 0x08, 0xEC, 0x49, 0x8F, 0x81, 0x5A, 0xE2, 0x40, 0xFF, 0x77, 0xFC, - 0x0D}; -}; \ No newline at end of file diff --git a/src/core/file_format/pkg.h b/src/core/file_format/pkg.h deleted file mode 100644 index a488a2df8..000000000 --- a/src/core/file_format/pkg.h +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include "common/endian.h" -#include "core/crypto/crypto.h" -#include "pfs.h" -#include "trp.h" - -struct PKGHeader { - u32_be magic; // Magic - u32_be pkg_type; - u32_be pkg_0x8; // unknown field - u32_be pkg_file_count; - u32_be pkg_table_entry_count; - u16_be pkg_sc_entry_count; - u16_be pkg_table_entry_count_2; // same as pkg_entry_count - u32_be pkg_table_entry_offset; // file table offset - u32_be pkg_sc_entry_data_size; - u64_be pkg_body_offset; // offset of PKG entries - u64_be pkg_body_size; // length of all PKG entries - u64_be pkg_content_offset; - u64_be pkg_content_size; - u8 pkg_content_id[0x24]; // packages' content ID as a 36-byte string - u8 pkg_padding[0xC]; // padding - u32_be pkg_drm_type; // DRM type - u32_be pkg_content_type; // Content type - u32_be pkg_content_flags; // Content flags - u32_be pkg_promote_size; - u32_be pkg_version_date; - u32_be pkg_version_hash; - u32_be pkg_0x088; - u32_be pkg_0x08C; - u32_be pkg_0x090; - u32_be pkg_0x094; - u32_be pkg_iro_tag; - u32_be pkg_drm_type_version; - - u8 pkg_zeroes_1[0x60]; - - /* Digest table */ - u8 digest_entries1[0x20]; // sha256 digest for main entry 1 - u8 digest_entries2[0x20]; // sha256 digest for main entry 2 - u8 digest_table_digest[0x20]; // sha256 digest for digest table - u8 digest_body_digest[0x20]; // sha256 digest for main table - - u8 pkg_zeroes_2[0x280]; - - u32_be pkg_0x400; - - u32_be pfs_image_count; // count of PFS images - u64_be pfs_image_flags; // PFS flags - u64_be pfs_image_offset; // offset to start of external PFS image - u64_be pfs_image_size; // size of external PFS image - u64_be mount_image_offset; - u64_be mount_image_size; - u64_be pkg_size; - u32_be pfs_signed_size; - u32_be pfs_cache_size; - u8 pfs_image_digest[0x20]; - u8 pfs_signed_digest[0x20]; - u64_be pfs_split_size_nth_0; - u64_be pfs_split_size_nth_1; - - u8 pkg_zeroes_3[0xB50]; - - u8 pkg_digest[0x20]; -}; - -enum class PKGContentFlag { - FIRST_PATCH = 0x100000, - PATCHGO = 0x200000, - REMASTER = 0x400000, - PS_CLOUD = 0x800000, - GD_AC = 0x2000000, - NON_GAME = 0x4000000, - UNKNOWN_0x8000000 = 0x8000000, - SUBSEQUENT_PATCH = 0x40000000, - DELTA_PATCH = 0x41000000, - CUMULATIVE_PATCH = 0x60000000 -}; - -struct PKGEntry { - u32_be id; // File ID, useful for files without a filename entry - u32_be filename_offset; // Offset into the filenames table (ID 0x200) where this file's name is - // located - u32_be flags1; // Flags including encrypted flag, etc - u32_be flags2; // Flags including encryption key index, etc - u32_be offset; // Offset into PKG to find the file - u32_be size; // Size of the file - u64_be padding; // blank padding -}; -static_assert(sizeof(PKGEntry) == 32); - -class PKG { -public: - PKG(); - ~PKG(); - - bool Open(const std::filesystem::path& filepath, std::string& failreason); - void ExtractFiles(const int index); - bool Extract(const std::filesystem::path& filepath, const std::filesystem::path& extract, - std::string& failreason); - - std::vector sfo; - - u32 GetNumberOfFiles() { - return fsTable.size(); - } - - u64 GetPkgSize() { - return pkgSize; - } - - std::string GetPkgFlags() { - return pkgFlags; - } - - std::string_view GetTitleID() { - return std::string_view(pkgTitleID, 9); - } - - PKGHeader GetPkgHeader() { - return pkgheader; - } - - static bool isFlagSet(u32_be variable, PKGContentFlag flag) { - return (variable) & static_cast(flag); - } - - static constexpr std::array, 10> flagNames = { - {{PKGContentFlag::FIRST_PATCH, "FIRST_PATCH"}, - {PKGContentFlag::PATCHGO, "PATCHGO"}, - {PKGContentFlag::REMASTER, "REMASTER"}, - {PKGContentFlag::PS_CLOUD, "PS_CLOUD"}, - {PKGContentFlag::GD_AC, "GD_AC"}, - {PKGContentFlag::NON_GAME, "NON_GAME"}, - {PKGContentFlag::UNKNOWN_0x8000000, "UNKNOWN_0x8000000"}, - {PKGContentFlag::SUBSEQUENT_PATCH, "SUBSEQUENT_PATCH"}, - {PKGContentFlag::DELTA_PATCH, "DELTA_PATCH"}, - {PKGContentFlag::CUMULATIVE_PATCH, "CUMULATIVE_PATCH"}}}; - -private: - Crypto crypto; - TRP trp; - u64 pkgSize = 0; - char pkgTitleID[9]; - PKGHeader pkgheader; - std::string pkgFlags; - - std::unordered_map extractPaths; - std::vector fsTable; - std::vector iNodeBuf; - std::vector sectorMap; - u64 pfsc_offset; - - std::array dk3_; - std::array ivKey; - std::array imgKey; - std::array ekpfsKey; - std::array dataKey; - std::array tweakKey; - std::vector decNp; - - std::filesystem::path pkgpath; - std::filesystem::path current_dir; - std::filesystem::path extract_path; -}; diff --git a/src/core/file_format/pkg_type.cpp b/src/core/file_format/pkg_type.cpp deleted file mode 100644 index 464f0b993..000000000 --- a/src/core/file_format/pkg_type.cpp +++ /dev/null @@ -1,638 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include "pkg_type.h" - -struct PkgEntryValue { - u32 type; - std::string_view name; - - operator u32() const noexcept { - return type; - } -}; - -constexpr static std::array PkgEntries = {{ - {0x0001, "digests"}, - {0x0010, "entry_keys"}, - {0x0020, "image_key"}, - {0x0080, "general_digests"}, - {0x0100, "metas"}, - {0x0200, "entry_names"}, - {0x0400, "license.dat"}, - {0x0401, "license.info"}, - {0x0402, "nptitle.dat"}, - {0x0403, "npbind.dat"}, - {0x0404, "selfinfo.dat"}, - {0x0406, "imageinfo.dat"}, - {0x0407, "target-deltainfo.dat"}, - {0x0408, "origin-deltainfo.dat"}, - {0x0409, "psreserved.dat"}, - {0x1000, "param.sfo"}, - {0x1001, "playgo-chunk.dat"}, - {0x1002, "playgo-chunk.sha"}, - {0x1003, "playgo-manifest.xml"}, - {0x1004, "pronunciation.xml"}, - {0x1005, "pronunciation.sig"}, - {0x1006, "pic1.png"}, - {0x1007, "pubtoolinfo.dat"}, - {0x1008, "app/playgo-chunk.dat"}, - {0x1009, "app/playgo-chunk.sha"}, - {0x100A, "app/playgo-manifest.xml"}, - {0x100B, "shareparam.json"}, - {0x100C, "shareoverlayimage.png"}, - {0x100D, "save_data.png"}, - {0x100E, "shareprivacyguardimage.png"}, - {0x1200, "icon0.png"}, - {0x1201, "icon0_00.png"}, - {0x1202, "icon0_01.png"}, - {0x1203, "icon0_02.png"}, - {0x1204, "icon0_03.png"}, - {0x1205, "icon0_04.png"}, - {0x1206, "icon0_05.png"}, - {0x1207, "icon0_06.png"}, - {0x1208, "icon0_07.png"}, - {0x1209, "icon0_08.png"}, - {0x120A, "icon0_09.png"}, - {0x120B, "icon0_10.png"}, - {0x120C, "icon0_11.png"}, - {0x120D, "icon0_12.png"}, - {0x120E, "icon0_13.png"}, - {0x120F, "icon0_14.png"}, - {0x1210, "icon0_15.png"}, - {0x1211, "icon0_16.png"}, - {0x1212, "icon0_17.png"}, - {0x1213, "icon0_18.png"}, - {0x1214, "icon0_19.png"}, - {0x1215, "icon0_20.png"}, - {0x1216, "icon0_21.png"}, - {0x1217, "icon0_22.png"}, - {0x1218, "icon0_23.png"}, - {0x1219, "icon0_24.png"}, - {0x121A, "icon0_25.png"}, - {0x121B, "icon0_26.png"}, - {0x121C, "icon0_27.png"}, - {0x121D, "icon0_28.png"}, - {0x121E, "icon0_29.png"}, - {0x121F, "icon0_30.png"}, - {0x1220, "pic0.png"}, - {0x1240, "snd0.at9"}, - {0x1241, "pic1_00.png"}, - {0x1242, "pic1_01.png"}, - {0x1243, "pic1_02.png"}, - {0x1244, "pic1_03.png"}, - {0x1245, "pic1_04.png"}, - {0x1246, "pic1_05.png"}, - {0x1247, "pic1_06.png"}, - {0x1248, "pic1_07.png"}, - {0x1249, "pic1_08.png"}, - {0x124A, "pic1_09.png"}, - {0x124B, "pic1_10.png"}, - {0x124C, "pic1_11.png"}, - {0x124D, "pic1_12.png"}, - {0x124E, "pic1_13.png"}, - {0x124F, "pic1_14.png"}, - {0x1250, "pic1_15.png"}, - {0x1251, "pic1_16.png"}, - {0x1252, "pic1_17.png"}, - {0x1253, "pic1_18.png"}, - {0x1254, "pic1_19.png"}, - {0x1255, "pic1_20.png"}, - {0x1256, "pic1_21.png"}, - {0x1257, "pic1_22.png"}, - {0x1258, "pic1_23.png"}, - {0x1259, "pic1_24.png"}, - {0x125A, "pic1_25.png"}, - {0x125B, "pic1_26.png"}, - {0x125C, "pic1_27.png"}, - {0x125D, "pic1_28.png"}, - {0x125E, "pic1_29.png"}, - {0x125F, "pic1_30.png"}, - {0x1260, "changeinfo/changeinfo.xml"}, - {0x1261, "changeinfo/changeinfo_00.xml"}, - {0x1262, "changeinfo/changeinfo_01.xml"}, - {0x1263, "changeinfo/changeinfo_02.xml"}, - {0x1264, "changeinfo/changeinfo_03.xml"}, - {0x1265, "changeinfo/changeinfo_04.xml"}, - {0x1266, "changeinfo/changeinfo_05.xml"}, - {0x1267, "changeinfo/changeinfo_06.xml"}, - {0x1268, "changeinfo/changeinfo_07.xml"}, - {0x1269, "changeinfo/changeinfo_08.xml"}, - {0x126A, "changeinfo/changeinfo_09.xml"}, - {0x126B, "changeinfo/changeinfo_10.xml"}, - {0x126C, "changeinfo/changeinfo_11.xml"}, - {0x126D, "changeinfo/changeinfo_12.xml"}, - {0x126E, "changeinfo/changeinfo_13.xml"}, - {0x126F, "changeinfo/changeinfo_14.xml"}, - {0x1270, "changeinfo/changeinfo_15.xml"}, - {0x1271, "changeinfo/changeinfo_16.xml"}, - {0x1272, "changeinfo/changeinfo_17.xml"}, - {0x1273, "changeinfo/changeinfo_18.xml"}, - {0x1274, "changeinfo/changeinfo_19.xml"}, - {0x1275, "changeinfo/changeinfo_20.xml"}, - {0x1276, "changeinfo/changeinfo_21.xml"}, - {0x1277, "changeinfo/changeinfo_22.xml"}, - {0x1278, "changeinfo/changeinfo_23.xml"}, - {0x1279, "changeinfo/changeinfo_24.xml"}, - {0x127A, "changeinfo/changeinfo_25.xml"}, - {0x127B, "changeinfo/changeinfo_26.xml"}, - {0x127C, "changeinfo/changeinfo_27.xml"}, - {0x127D, "changeinfo/changeinfo_28.xml"}, - {0x127E, "changeinfo/changeinfo_29.xml"}, - {0x127F, "changeinfo/changeinfo_30.xml"}, - {0x1280, "icon0.dds"}, - {0x1281, "icon0_00.dds"}, - {0x1282, "icon0_01.dds"}, - {0x1283, "icon0_02.dds"}, - {0x1284, "icon0_03.dds"}, - {0x1285, "icon0_04.dds"}, - {0x1286, "icon0_05.dds"}, - {0x1287, "icon0_06.dds"}, - {0x1288, "icon0_07.dds"}, - {0x1289, "icon0_08.dds"}, - {0x128A, "icon0_09.dds"}, - {0x128B, "icon0_10.dds"}, - {0x128C, "icon0_11.dds"}, - {0x128D, "icon0_12.dds"}, - {0x128E, "icon0_13.dds"}, - {0x128F, "icon0_14.dds"}, - {0x1290, "icon0_15.dds"}, - {0x1291, "icon0_16.dds"}, - {0x1292, "icon0_17.dds"}, - {0x1293, "icon0_18.dds"}, - {0x1294, "icon0_19.dds"}, - {0x1295, "icon0_20.dds"}, - {0x1296, "icon0_21.dds"}, - {0x1297, "icon0_22.dds"}, - {0x1298, "icon0_23.dds"}, - {0x1299, "icon0_24.dds"}, - {0x129A, "icon0_25.dds"}, - {0x129B, "icon0_26.dds"}, - {0x129C, "icon0_27.dds"}, - {0x129D, "icon0_28.dds"}, - {0x129E, "icon0_29.dds"}, - {0x129F, "icon0_30.dds"}, - {0x12A0, "pic0.dds"}, - {0x12C0, "pic1.dds"}, - {0x12C1, "pic1_00.dds"}, - {0x12C2, "pic1_01.dds"}, - {0x12C3, "pic1_02.dds"}, - {0x12C4, "pic1_03.dds"}, - {0x12C5, "pic1_04.dds"}, - {0x12C6, "pic1_05.dds"}, - {0x12C7, "pic1_06.dds"}, - {0x12C8, "pic1_07.dds"}, - {0x12C9, "pic1_08.dds"}, - {0x12CA, "pic1_09.dds"}, - {0x12CB, "pic1_10.dds"}, - {0x12CC, "pic1_11.dds"}, - {0x12CD, "pic1_12.dds"}, - {0x12CE, "pic1_13.dds"}, - {0x12CF, "pic1_14.dds"}, - {0x12D0, "pic1_15.dds"}, - {0x12D1, "pic1_16.dds"}, - {0x12D2, "pic1_17.dds"}, - {0x12D3, "pic1_18.dds"}, - {0x12D4, "pic1_19.dds"}, - {0x12D5, "pic1_20.dds"}, - {0x12D6, "pic1_21.dds"}, - {0x12D7, "pic1_22.dds"}, - {0x12D8, "pic1_23.dds"}, - {0x12D9, "pic1_24.dds"}, - {0x12DA, "pic1_25.dds"}, - {0x12DB, "pic1_26.dds"}, - {0x12DC, "pic1_27.dds"}, - {0x12DD, "pic1_28.dds"}, - {0x12DE, "pic1_29.dds"}, - {0x12DF, "pic1_30.dds"}, - {0x1400, "trophy/trophy00.trp"}, - {0x1401, "trophy/trophy01.trp"}, - {0x1402, "trophy/trophy02.trp"}, - {0x1403, "trophy/trophy03.trp"}, - {0x1404, "trophy/trophy04.trp"}, - {0x1405, "trophy/trophy05.trp"}, - {0x1406, "trophy/trophy06.trp"}, - {0x1407, "trophy/trophy07.trp"}, - {0x1408, "trophy/trophy08.trp"}, - {0x1409, "trophy/trophy09.trp"}, - {0x140A, "trophy/trophy10.trp"}, - {0x140B, "trophy/trophy11.trp"}, - {0x140C, "trophy/trophy12.trp"}, - {0x140D, "trophy/trophy13.trp"}, - {0x140E, "trophy/trophy14.trp"}, - {0x140F, "trophy/trophy15.trp"}, - {0x1410, "trophy/trophy16.trp"}, - {0x1411, "trophy/trophy17.trp"}, - {0x1412, "trophy/trophy18.trp"}, - {0x1413, "trophy/trophy19.trp"}, - {0x1414, "trophy/trophy20.trp"}, - {0x1415, "trophy/trophy21.trp"}, - {0x1416, "trophy/trophy22.trp"}, - {0x1417, "trophy/trophy23.trp"}, - {0x1418, "trophy/trophy24.trp"}, - {0x1419, "trophy/trophy25.trp"}, - {0x141A, "trophy/trophy26.trp"}, - {0x141B, "trophy/trophy27.trp"}, - {0x141C, "trophy/trophy28.trp"}, - {0x141D, "trophy/trophy29.trp"}, - {0x141E, "trophy/trophy30.trp"}, - {0x141F, "trophy/trophy31.trp"}, - {0x1420, "trophy/trophy32.trp"}, - {0x1421, "trophy/trophy33.trp"}, - {0x1422, "trophy/trophy34.trp"}, - {0x1423, "trophy/trophy35.trp"}, - {0x1424, "trophy/trophy36.trp"}, - {0x1425, "trophy/trophy37.trp"}, - {0x1426, "trophy/trophy38.trp"}, - {0x1427, "trophy/trophy39.trp"}, - {0x1428, "trophy/trophy40.trp"}, - {0x1429, "trophy/trophy41.trp"}, - {0x142A, "trophy/trophy42.trp"}, - {0x142B, "trophy/trophy43.trp"}, - {0x142C, "trophy/trophy44.trp"}, - {0x142D, "trophy/trophy45.trp"}, - {0x142E, "trophy/trophy46.trp"}, - {0x142F, "trophy/trophy47.trp"}, - {0x1430, "trophy/trophy48.trp"}, - {0x1431, "trophy/trophy49.trp"}, - {0x1432, "trophy/trophy50.trp"}, - {0x1433, "trophy/trophy51.trp"}, - {0x1434, "trophy/trophy52.trp"}, - {0x1435, "trophy/trophy53.trp"}, - {0x1436, "trophy/trophy54.trp"}, - {0x1437, "trophy/trophy55.trp"}, - {0x1438, "trophy/trophy56.trp"}, - {0x1439, "trophy/trophy57.trp"}, - {0x143A, "trophy/trophy58.trp"}, - {0x143B, "trophy/trophy59.trp"}, - {0x143C, "trophy/trophy60.trp"}, - {0x143D, "trophy/trophy61.trp"}, - {0x143E, "trophy/trophy62.trp"}, - {0x143F, "trophy/trophy63.trp"}, - {0x1440, "trophy/trophy64.trp"}, - {0x1441, "trophy/trophy65.trp"}, - {0x1442, "trophy/trophy66.trp"}, - {0x1443, "trophy/trophy67.trp"}, - {0x1444, "trophy/trophy68.trp"}, - {0x1445, "trophy/trophy69.trp"}, - {0x1446, "trophy/trophy70.trp"}, - {0x1447, "trophy/trophy71.trp"}, - {0x1448, "trophy/trophy72.trp"}, - {0x1449, "trophy/trophy73.trp"}, - {0x144A, "trophy/trophy74.trp"}, - {0x144B, "trophy/trophy75.trp"}, - {0x144C, "trophy/trophy76.trp"}, - {0x144D, "trophy/trophy77.trp"}, - {0x144E, "trophy/trophy78.trp"}, - {0x144F, "trophy/trophy79.trp"}, - {0x1450, "trophy/trophy80.trp"}, - {0x1451, "trophy/trophy81.trp"}, - {0x1452, "trophy/trophy82.trp"}, - {0x1453, "trophy/trophy83.trp"}, - {0x1454, "trophy/trophy84.trp"}, - {0x1455, "trophy/trophy85.trp"}, - {0x1456, "trophy/trophy86.trp"}, - {0x1457, "trophy/trophy87.trp"}, - {0x1458, "trophy/trophy88.trp"}, - {0x1459, "trophy/trophy89.trp"}, - {0x145A, "trophy/trophy90.trp"}, - {0x145B, "trophy/trophy91.trp"}, - {0x145C, "trophy/trophy92.trp"}, - {0x145D, "trophy/trophy93.trp"}, - {0x145E, "trophy/trophy94.trp"}, - {0x145F, "trophy/trophy95.trp"}, - {0x1460, "trophy/trophy96.trp"}, - {0x1461, "trophy/trophy97.trp"}, - {0x1462, "trophy/trophy98.trp"}, - {0x1463, "trophy/trophy99.trp"}, - {0x1600, "keymap_rp/001.png"}, - {0x1601, "keymap_rp/002.png"}, - {0x1602, "keymap_rp/003.png"}, - {0x1603, "keymap_rp/004.png"}, - {0x1604, "keymap_rp/005.png"}, - {0x1605, "keymap_rp/006.png"}, - {0x1606, "keymap_rp/007.png"}, - {0x1607, "keymap_rp/008.png"}, - {0x1608, "keymap_rp/009.png"}, - {0x1609, "keymap_rp/010.png"}, - {0x1610, "keymap_rp/00/001.png"}, - {0x1611, "keymap_rp/00/002.png"}, - {0x1612, "keymap_rp/00/003.png"}, - {0x1613, "keymap_rp/00/004.png"}, - {0x1614, "keymap_rp/00/005.png"}, - {0x1615, "keymap_rp/00/006.png"}, - {0x1616, "keymap_rp/00/007.png"}, - {0x1617, "keymap_rp/00/008.png"}, - {0x1618, "keymap_rp/00/009.png"}, - {0x1619, "keymap_rp/00/010.png"}, - {0x1620, "keymap_rp/01/001.png"}, - {0x1621, "keymap_rp/01/002.png"}, - {0x1622, "keymap_rp/01/003.png"}, - {0x1623, "keymap_rp/01/004.png"}, - {0x1624, "keymap_rp/01/005.png"}, - {0x1625, "keymap_rp/01/006.png"}, - {0x1626, "keymap_rp/01/007.png"}, - {0x1627, "keymap_rp/01/008.png"}, - {0x1628, "keymap_rp/01/009.png"}, - {0x1629, "keymap_rp/01/010.png"}, - {0x1630, "keymap_rp/02/001.png"}, - {0x1631, "keymap_rp/02/002.png"}, - {0x1632, "keymap_rp/02/003.png"}, - {0x1633, "keymap_rp/02/004.png"}, - {0x1634, "keymap_rp/02/005.png"}, - {0x1635, "keymap_rp/02/006.png"}, - {0x1636, "keymap_rp/02/007.png"}, - {0x1637, "keymap_rp/02/008.png"}, - {0x1638, "keymap_rp/02/009.png"}, - {0x1639, "keymap_rp/02/010.png"}, - {0x1640, "keymap_rp/03/001.png"}, - {0x1641, "keymap_rp/03/002.png"}, - {0x1642, "keymap_rp/03/003.png"}, - {0x1643, "keymap_rp/03/004.png"}, - {0x1644, "keymap_rp/03/005.png"}, - {0x1645, "keymap_rp/03/006.png"}, - {0x1646, "keymap_rp/03/007.png"}, - {0x1647, "keymap_rp/03/008.png"}, - {0x1648, "keymap_rp/03/0010.png"}, - {0x1650, "keymap_rp/04/001.png"}, - {0x1651, "keymap_rp/04/002.png"}, - {0x1652, "keymap_rp/04/003.png"}, - {0x1653, "keymap_rp/04/004.png"}, - {0x1654, "keymap_rp/04/005.png"}, - {0x1655, "keymap_rp/04/006.png"}, - {0x1656, "keymap_rp/04/007.png"}, - {0x1657, "keymap_rp/04/008.png"}, - {0x1658, "keymap_rp/04/009.png"}, - {0x1659, "keymap_rp/04/010.png"}, - {0x1660, "keymap_rp/05/001.png"}, - {0x1661, "keymap_rp/05/002.png"}, - {0x1662, "keymap_rp/05/003.png"}, - {0x1663, "keymap_rp/05/004.png"}, - {0x1664, "keymap_rp/05/005.png"}, - {0x1665, "keymap_rp/05/006.png"}, - {0x1666, "keymap_rp/05/007.png"}, - {0x1667, "keymap_rp/05/008.png"}, - {0x1668, "keymap_rp/05/009.png"}, - {0x1669, "keymap_rp/05/010.png"}, - {0x1670, "keymap_rp/06/001.png"}, - {0x1671, "keymap_rp/06/002.png"}, - {0x1672, "keymap_rp/06/003.png"}, - {0x1673, "keymap_rp/06/004.png"}, - {0x1674, "keymap_rp/06/005.png"}, - {0x1675, "keymap_rp/06/006.png"}, - {0x1676, "keymap_rp/06/007.png"}, - {0x1677, "keymap_rp/06/008.png"}, - {0x1678, "keymap_rp/06/009.png"}, - {0x1679, "keymap_rp/06/010.png"}, - {0x1680, "keymap_rp/07/001.png"}, - {0x1681, "keymap_rp/07/002.png"}, - {0x1682, "keymap_rp/07/003.png"}, - {0x1683, "keymap_rp/07/004.png"}, - {0x1684, "keymap_rp/07/005.png"}, - {0x1685, "keymap_rp/07/006.png"}, - {0x1686, "keymap_rp/07/007.png"}, - {0x1687, "keymap_rp/07/008.png"}, - {0x1688, "keymap_rp/07/009.png"}, - {0x1689, "keymap_rp/07/010.png"}, - {0x1690, "keymap_rp/08/001.png"}, - {0x1691, "keymap_rp/08/002.png"}, - {0x1692, "keymap_rp/08/003.png"}, - {0x1693, "keymap_rp/08/004.png"}, - {0x1694, "keymap_rp/08/005.png"}, - {0x1695, "keymap_rp/08/006.png"}, - {0x1696, "keymap_rp/08/007.png"}, - {0x1697, "keymap_rp/08/008.png"}, - {0x1698, "keymap_rp/08/009.png"}, - {0x1699, "keymap_rp/08/010.png"}, - {0x16A0, "keymap_rp/09/001.png"}, - {0x16A1, "keymap_rp/09/002.png"}, - {0x16A2, "keymap_rp/09/003.png"}, - {0x16A3, "keymap_rp/09/004.png"}, - {0x16A4, "keymap_rp/09/005.png"}, - {0x16A5, "keymap_rp/09/006.png"}, - {0x16A6, "keymap_rp/09/007.png"}, - {0x16A7, "keymap_rp/09/008.png"}, - {0x16A8, "keymap_rp/09/009.png"}, - {0x16A9, "keymap_rp/09/010.png"}, - {0x16B0, "keymap_rp/10/001.png"}, - {0x16B1, "keymap_rp/10/002.png"}, - {0x16B2, "keymap_rp/10/003.png"}, - {0x16B3, "keymap_rp/10/004.png"}, - {0x16B4, "keymap_rp/10/005.png"}, - {0x16B5, "keymap_rp/10/006.png"}, - {0x16B6, "keymap_rp/10/007.png"}, - {0x16B7, "keymap_rp/10/008.png"}, - {0x16B8, "keymap_rp/10/009.png"}, - {0x16B9, "keymap_rp/10/010.png"}, - {0x16C0, "keymap_rp/11/001.png"}, - {0x16C1, "keymap_rp/11/002.png"}, - {0x16C2, "keymap_rp/11/003.png"}, - {0x16C3, "keymap_rp/11/004.png"}, - {0x16C4, "keymap_rp/11/005.png"}, - {0x16C5, "keymap_rp/11/006.png"}, - {0x16C6, "keymap_rp/11/007.png"}, - {0x16C7, "keymap_rp/11/008.png"}, - {0x16C8, "keymap_rp/11/009.png"}, - {0x16C9, "keymap_rp/11/010.png"}, - {0x16D0, "keymap_rp/12/001.png"}, - {0x16D1, "keymap_rp/12/002.png"}, - {0x16D2, "keymap_rp/12/003.png"}, - {0x16D3, "keymap_rp/12/004.png"}, - {0x16D4, "keymap_rp/12/005.png"}, - {0x16D5, "keymap_rp/12/006.png"}, - {0x16D6, "keymap_rp/12/007.png"}, - {0x16D7, "keymap_rp/12/008.png"}, - {0x16D8, "keymap_rp/12/009.png"}, - {0x16D9, "keymap_rp/12/010.png"}, - {0x16E0, "keymap_rp/13/001.png"}, - {0x16E1, "keymap_rp/13/002.png"}, - {0x16E2, "keymap_rp/13/003.png"}, - {0x16E3, "keymap_rp/13/004.png"}, - {0x16E4, "keymap_rp/13/005.png"}, - {0x16E5, "keymap_rp/13/006.png"}, - {0x16E6, "keymap_rp/13/007.png"}, - {0x16E7, "keymap_rp/13/008.png"}, - {0x16E8, "keymap_rp/13/009.png"}, - {0x16E9, "keymap_rp/13/010.png"}, - {0x16F0, "keymap_rp/14/001.png"}, - {0x16F1, "keymap_rp/14/002.png"}, - {0x16F2, "keymap_rp/14/003.png"}, - {0x16F3, "keymap_rp/14/004.png"}, - {0x16F4, "keymap_rp/14/005.png"}, - {0x16F5, "keymap_rp/14/006.png"}, - {0x16F6, "keymap_rp/14/007.png"}, - {0x16F7, "keymap_rp/14/008.png"}, - {0x16F8, "keymap_rp/14/009.png"}, - {0x16F9, "keymap_rp/14/010.png"}, - {0x1700, "keymap_rp/15/001.png"}, - {0x1701, "keymap_rp/15/002.png"}, - {0x1702, "keymap_rp/15/003.png"}, - {0x1703, "keymap_rp/15/004.png"}, - {0x1704, "keymap_rp/15/005.png"}, - {0x1705, "keymap_rp/15/006.png"}, - {0x1706, "keymap_rp/15/007.png"}, - {0x1707, "keymap_rp/15/008.png"}, - {0x1708, "keymap_rp/15/009.png"}, - {0x1709, "keymap_rp/15/010.png"}, - {0x1710, "keymap_rp/16/001.png"}, - {0x1711, "keymap_rp/16/002.png"}, - {0x1712, "keymap_rp/16/003.png"}, - {0x1713, "keymap_rp/16/004.png"}, - {0x1714, "keymap_rp/16/005.png"}, - {0x1715, "keymap_rp/16/006.png"}, - {0x1716, "keymap_rp/16/007.png"}, - {0x1717, "keymap_rp/16/008.png"}, - {0x1718, "keymap_rp/16/009.png"}, - {0x1719, "keymap_rp/16/010.png"}, - {0x1720, "keymap_rp/17/001.png"}, - {0x1721, "keymap_rp/17/002.png"}, - {0x1722, "keymap_rp/17/003.png"}, - {0x1723, "keymap_rp/17/004.png"}, - {0x1724, "keymap_rp/17/005.png"}, - {0x1725, "keymap_rp/17/006.png"}, - {0x1726, "keymap_rp/17/007.png"}, - {0x1727, "keymap_rp/17/008.png"}, - {0x1728, "keymap_rp/17/009.png"}, - {0x1729, "keymap_rp/17/010.png"}, - {0x1730, "keymap_rp/18/001.png"}, - {0x1731, "keymap_rp/18/002.png"}, - {0x1732, "keymap_rp/18/003.png"}, - {0x1733, "keymap_rp/18/004.png"}, - {0x1734, "keymap_rp/18/005.png"}, - {0x1735, "keymap_rp/18/006.png"}, - {0x1736, "keymap_rp/18/007.png"}, - {0x1737, "keymap_rp/18/008.png"}, - {0x1738, "keymap_rp/18/009.png"}, - {0x1739, "keymap_rp/18/010.png"}, - {0x1740, "keymap_rp/19/001.png"}, - {0x1741, "keymap_rp/19/002.png"}, - {0x1742, "keymap_rp/19/003.png"}, - {0x1743, "keymap_rp/19/004.png"}, - {0x1744, "keymap_rp/19/005.png"}, - {0x1745, "keymap_rp/19/006.png"}, - {0x1746, "keymap_rp/19/007.png"}, - {0x1747, "keymap_rp/19/008.png"}, - {0x1748, "keymap_rp/19/009.png"}, - {0x1749, "keymap_rp/19/010.png"}, - {0x1750, "keymap_rp/20/001.png"}, - {0x1751, "keymap_rp/20/002.png"}, - {0x1752, "keymap_rp/20/003.png"}, - {0x1753, "keymap_rp/20/004.png"}, - {0x1754, "keymap_rp/20/005.png"}, - {0x1755, "keymap_rp/20/006.png"}, - {0x1756, "keymap_rp/20/007.png"}, - {0x1757, "keymap_rp/20/008.png"}, - {0x1758, "keymap_rp/20/009.png"}, - {0x1759, "keymap_rp/20/010.png"}, - {0x1760, "keymap_rp/21/001.png"}, - {0x1761, "keymap_rp/21/002.png"}, - {0x1762, "keymap_rp/21/003.png"}, - {0x1763, "keymap_rp/21/004.png"}, - {0x1764, "keymap_rp/21/005.png"}, - {0x1765, "keymap_rp/21/006.png"}, - {0x1766, "keymap_rp/21/007.png"}, - {0x1767, "keymap_rp/21/008.png"}, - {0x1768, "keymap_rp/21/009.png"}, - {0x1769, "keymap_rp/21/010.png"}, - {0x1770, "keymap_rp/22/001.png"}, - {0x1771, "keymap_rp/22/002.png"}, - {0x1772, "keymap_rp/22/003.png"}, - {0x1773, "keymap_rp/22/004.png"}, - {0x1774, "keymap_rp/22/005.png"}, - {0x1775, "keymap_rp/22/006.png"}, - {0x1776, "keymap_rp/22/007.png"}, - {0x1777, "keymap_rp/22/008.png"}, - {0x1778, "keymap_rp/22/009.png"}, - {0x1779, "keymap_rp/22/010.png"}, - {0x1780, "keymap_rp/23/001.png"}, - {0x1781, "keymap_rp/23/002.png"}, - {0x1782, "keymap_rp/23/003.png"}, - {0x1783, "keymap_rp/23/004.png"}, - {0x1784, "keymap_rp/23/005.png"}, - {0x1785, "keymap_rp/23/006.png"}, - {0x1786, "keymap_rp/23/007.png"}, - {0x1787, "keymap_rp/23/008.png"}, - {0x1788, "keymap_rp/23/009.png"}, - {0x1789, "keymap_rp/23/010.png"}, - {0x1790, "keymap_rp/24/001.png"}, - {0x1791, "keymap_rp/24/002.png"}, - {0x1792, "keymap_rp/24/003.png"}, - {0x1793, "keymap_rp/24/004.png"}, - {0x1794, "keymap_rp/24/005.png"}, - {0x1795, "keymap_rp/24/006.png"}, - {0x1796, "keymap_rp/24/007.png"}, - {0x1797, "keymap_rp/24/008.png"}, - {0x1798, "keymap_rp/24/009.png"}, - {0x1799, "keymap_rp/24/010.png"}, - {0x17A0, "keymap_rp/25/001.png"}, - {0x17A1, "keymap_rp/25/002.png"}, - {0x17A2, "keymap_rp/25/003.png"}, - {0x17A3, "keymap_rp/25/004.png"}, - {0x17A4, "keymap_rp/25/005.png"}, - {0x17A5, "keymap_rp/25/006.png"}, - {0x17A6, "keymap_rp/25/007.png"}, - {0x17A7, "keymap_rp/25/008.png"}, - {0x17A8, "keymap_rp/25/009.png"}, - {0x17A9, "keymap_rp/25/010.png"}, - {0x17B0, "keymap_rp/26/001.png"}, - {0x17B1, "keymap_rp/26/002.png"}, - {0x17B2, "keymap_rp/26/003.png"}, - {0x17B3, "keymap_rp/26/004.png"}, - {0x17B4, "keymap_rp/26/005.png"}, - {0x17B5, "keymap_rp/26/006.png"}, - {0x17B6, "keymap_rp/26/007.png"}, - {0x17B7, "keymap_rp/26/008.png"}, - {0x17B8, "keymap_rp/26/009.png"}, - {0x17B9, "keymap_rp/26/010.png"}, - {0x17C0, "keymap_rp/27/001.png"}, - {0x17C1, "keymap_rp/27/002.png"}, - {0x17C2, "keymap_rp/27/003.png"}, - {0x17C3, "keymap_rp/27/004.png"}, - {0x17C4, "keymap_rp/27/005.png"}, - {0x17C5, "keymap_rp/27/006.png"}, - {0x17C6, "keymap_rp/27/007.png"}, - {0x17C7, "keymap_rp/27/008.png"}, - {0x17C8, "keymap_rp/27/009.png"}, - {0x17C9, "keymap_rp/27/010.png"}, - {0x17D0, "keymap_rp/28/001.png"}, - {0x17D1, "keymap_rp/28/002.png"}, - {0x17D2, "keymap_rp/28/003.png"}, - {0x17D3, "keymap_rp/28/004.png"}, - {0x17D4, "keymap_rp/28/005.png"}, - {0x17D5, "keymap_rp/28/006.png"}, - {0x17D6, "keymap_rp/28/007.png"}, - {0x17D7, "keymap_rp/28/008.png"}, - {0x17D8, "keymap_rp/28/009.png"}, - {0x17D9, "keymap_rp/28/010.png"}, - {0x17E0, "keymap_rp/29/001.png"}, - {0x17E1, "keymap_rp/29/002.png"}, - {0x17E2, "keymap_rp/29/003.png"}, - {0x17E3, "keymap_rp/29/004.png"}, - {0x17E4, "keymap_rp/29/005.png"}, - {0x17E5, "keymap_rp/29/006.png"}, - {0x17E6, "keymap_rp/29/007.png"}, - {0x17E7, "keymap_rp/29/008.png"}, - {0x17E8, "keymap_rp/29/009.png"}, - {0x17E9, "keymap_rp/29/010.png"}, - {0x17F0, "keymap_rp/30/001.png"}, - {0x17F1, "keymap_rp/30/002.png"}, - {0x17F2, "keymap_rp/30/003.png"}, - {0x17F3, "keymap_rp/30/004.png"}, - {0x17F4, "keymap_rp/30/005.png"}, - {0x17F5, "keymap_rp/30/006.png"}, - {0x17F6, "keymap_rp/30/007.png"}, - {0x17F7, "keymap_rp/30/008.png"}, - {0x17F8, "keymap_rp/30/009.png"}, - {0x17F9, "keymap_rp/30/010.png"}, -}}; - -std::string_view GetEntryNameByType(u32 type) { - const auto key = PkgEntryValue{type}; - const auto it = std::ranges::lower_bound(PkgEntries, key); - if (it != PkgEntries.end() && it->type == type) { - return it->name; - } - return ""; -} diff --git a/src/core/file_format/pkg_type.h b/src/core/file_format/pkg_type.h deleted file mode 100644 index 6b010e3a3..000000000 --- a/src/core/file_format/pkg_type.h +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/types.h" - -/// Retrieves the PKG entry name from its type identifier. -std::string_view GetEntryNameByType(u32 type); diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp index d25c93c3f..311bd0b9d 100644 --- a/src/core/file_format/trp.cpp +++ b/src/core/file_format/trp.cpp @@ -1,10 +1,36 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include + #include "common/config.h" #include "common/logging/log.h" #include "common/path_util.h" -#include "trp.h" +#include "core/file_format/trp.h" + +static void DecryptEFSM(std::span trophyKey, + std::span NPcommID, + std::span efsmIv, std::span ciphertext, + std::span decrypted) { + + // step 1: Encrypt NPcommID + CryptoPP::CBC_Mode::Encryption encrypt; + + std::vector trophyIv(16, 0); + std::vector trpKey(16); + + encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data()); + encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16); + + // step 2: decrypt efsm. + CryptoPP::CBC_Mode::Decryption decrypt; + decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data()); + + for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { + decrypt.ProcessData(decrypted.data() + i, ciphertext.data() + i, CryptoPP::AES::BLOCKSIZE); + } +} TRP::TRP() = default; TRP::~TRP() = default; @@ -115,7 +141,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit return false; } file.Read(ESFM); - crypto.decryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt + DecryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt removePadding(XML); std::string xml_name = entry.entry_name; size_t pos = xml_name.find("ESFM"); diff --git a/src/core/file_format/trp.h b/src/core/file_format/trp.h index aec129f0e..01207475b 100644 --- a/src/core/file_format/trp.h +++ b/src/core/file_format/trp.h @@ -7,7 +7,6 @@ #include "common/endian.h" #include "common/io_file.h" #include "common/types.h" -#include "core/crypto/crypto.h" struct TrpHeader { u32_be magic; // (0xDCA24D00) @@ -37,10 +36,9 @@ public: void GetNPcommID(const std::filesystem::path& trophyPath, int index); private: - Crypto crypto; std::vector NPcommID = std::vector(12); std::array np_comm_id{}; std::array esfmIv{}; std::filesystem::path trpFilesPath; static constexpr int iv_len = 16; -}; \ No newline at end of file +}; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 3420e933e..e92676c02 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "about_dialog.h" #include "cheats_patches.h" @@ -19,7 +20,6 @@ #include "common/string_util.h" #include "common/version.h" #include "control_settings.h" -#include "core/file_format/pkg.h" #include "core/loader.h" #include "game_install_dialog.h" #include "install_dir_select.h" @@ -718,7 +718,6 @@ void MainWindow::CreateConnects() { }); // Package install. - connect(ui->bootInstallPkgAct, &QAction::triggered, this, &MainWindow::InstallPkg); connect(ui->bootGameAct, &QAction::triggered, this, &MainWindow::BootGame); connect(ui->gameInstallPathAct, &QAction::triggered, this, &MainWindow::InstallDirectory); @@ -726,15 +725,6 @@ void MainWindow::CreateConnects() { connect(ui->addElfFolderAct, &QAction::triggered, m_elf_viewer.data(), &ElfViewer::OpenElfFolder); - // Package Viewer. - connect(ui->pkgViewerAct, &QAction::triggered, this, [this]() { - PKGViewer* pkgViewer = new PKGViewer( - m_game_info, this, [this](std::filesystem::path file, int pkgNum, int nPkg) { - this->InstallDragDropPkg(file, pkgNum, nPkg); - }); - pkgViewer->show(); - }); - // Trophy Viewer connect(ui->trophyViewerAct, &QAction::triggered, this, [this]() { if (m_game_info->m_games.empty()) { @@ -965,22 +955,6 @@ void MainWindow::SaveWindowState() const { this->geometry().width(), this->geometry().height()); } -void MainWindow::InstallPkg() { - QFileDialog dialog; - dialog.setFileMode(QFileDialog::ExistingFiles); - dialog.setNameFilter(tr("PKG File (*.PKG *.pkg)")); - if (dialog.exec()) { - QStringList fileNames = dialog.selectedFiles(); - int nPkg = fileNames.size(); - int pkgNum = 0; - for (const QString& file : fileNames) { - ++pkgNum; - std::filesystem::path path = Common::FS::PathFromQString(file); - MainWindow::InstallDragDropPkg(path, pkgNum, nPkg); - } - } -} - void MainWindow::BootGame() { QFileDialog dialog; dialog.setFileMode(QFileDialog::ExistingFile); @@ -1004,260 +978,6 @@ void MainWindow::BootGame() { } } -void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg) { - if (Loader::DetectFileType(file) == Loader::FileTypes::Pkg) { - std::string failreason; - pkg = PKG(); - if (!pkg.Open(file, failreason)) { - QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); - return; - } - if (!psf.Open(pkg.sfo)) { - QMessageBox::critical(this, tr("PKG ERROR"), - "Could not read SFO. Check log for details"); - return; - } - auto category = psf.GetString("CATEGORY"); - - if (!use_for_all_queued || pkgNum == 1) { - InstallDirSelect ids; - const auto selected = ids.exec(); - if (selected == QDialog::Rejected) { - return; - } - - last_install_dir = ids.getSelectedDirectory(); - delete_file_on_install = ids.deleteFileOnInstall(); - use_for_all_queued = ids.useForAllQueued(); - } - std::filesystem::path game_install_dir = last_install_dir; - - QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); - bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled(); - - // Default paths - auto game_folder_path = game_install_dir / pkg.GetTitleID(); - auto game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-patch") - : game_folder_path; - const int max_depth = 5; - - if (pkgType.contains("PATCH")) { - // For patches, try to find the game recursively - auto found_game = Common::FS::FindGameByID(game_install_dir, - std::string{pkg.GetTitleID()}, max_depth); - if (found_game.has_value()) { - game_folder_path = found_game.value().parent_path(); - game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-patch") - : game_folder_path; - } - } else { - // For base games, we check if the game is already installed - auto found_game = Common::FS::FindGameByID(game_install_dir, - std::string{pkg.GetTitleID()}, max_depth); - if (found_game.has_value()) { - game_folder_path = found_game.value().parent_path(); - } - // If the game is not found, we install it in the game install directory - else { - game_folder_path = game_install_dir / pkg.GetTitleID(); - } - game_update_path = use_game_update ? game_folder_path.parent_path() / - (std::string{pkg.GetTitleID()} + "-patch") - : game_folder_path; - } - - QString gameDirPath; - Common::FS::PathToQString(gameDirPath, game_folder_path); - QDir game_dir(gameDirPath); - if (game_dir.exists()) { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("PKG Extraction")); - - std::string content_id; - if (auto value = psf.GetString("CONTENT_ID"); value.has_value()) { - content_id = std::string{*value}; - } else { - QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no CONTENT_ID"); - return; - } - std::string entitlement_label = Common::SplitString(content_id, '-')[2]; - - auto addon_extract_path = - Config::getAddonInstallDir() / pkg.GetTitleID() / entitlement_label; - QString addonDirPath; - Common::FS::PathToQString(addonDirPath, addon_extract_path); - QDir addon_dir(addonDirPath); - - if (pkgType.contains("PATCH")) { - QString pkg_app_version; - if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) { - pkg_app_version = QString::fromStdString(std::string{*app_ver}); - } else { - QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no APP_VER"); - return; - } - std::filesystem::path sce_folder_path = - std::filesystem::exists(game_update_path / "sce_sys" / "param.sfo") - ? game_update_path / "sce_sys" / "param.sfo" - : game_folder_path / "sce_sys" / "param.sfo"; - psf.Open(sce_folder_path); - QString game_app_version; - if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) { - game_app_version = QString::fromStdString(std::string{*app_ver}); - } else { - QMessageBox::critical(this, tr("PKG ERROR"), "PSF file there is no APP_VER"); - return; - } - double appD = game_app_version.toDouble(); - double pkgD = pkg_app_version.toDouble(); - if (pkgD == appD) { - msgBox.setText(QString(tr("Patch detected!") + "\n" + - tr("PKG and Game versions match: ") + pkg_app_version + - "\n" + tr("Would you like to overwrite?"))); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::No); - } else if (pkgD < appD) { - msgBox.setText(QString(tr("Patch detected!") + "\n" + - tr("PKG Version %1 is older than installed version: ") - .arg(pkg_app_version) + - game_app_version + "\n" + - tr("Would you like to overwrite?"))); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::No); - } else { - msgBox.setText(QString(tr("Patch detected!") + "\n" + - tr("Game is installed: ") + game_app_version + "\n" + - tr("Would you like to install Patch: ") + - pkg_app_version + " ?")); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::No); - } - int result = msgBox.exec(); - if (result == QMessageBox::Yes) { - // Do nothing. - } else { - return; - } - } else if (category == "ac") { - if (!addon_dir.exists()) { - QMessageBox addonMsgBox; - addonMsgBox.setWindowTitle(tr("DLC Installation")); - addonMsgBox.setText(QString(tr("Would you like to install DLC: %1?")) - .arg(QString::fromStdString(entitlement_label))); - - addonMsgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - addonMsgBox.setDefaultButton(QMessageBox::No); - int result = addonMsgBox.exec(); - if (result == QMessageBox::Yes) { - game_update_path = addon_extract_path; - } else { - return; - } - } else { - msgBox.setText(QString(tr("DLC already installed:") + "\n" + addonDirPath + - "\n\n" + tr("Would you like to overwrite?"))); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::No); - int result = msgBox.exec(); - if (result == QMessageBox::Yes) { - game_update_path = addon_extract_path; - } else { - return; - } - } - } else { - msgBox.setText(QString(tr("Game already installed") + "\n" + gameDirPath + "\n" + - tr("Would you like to overwrite?"))); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::No); - int result = msgBox.exec(); - if (result == QMessageBox::Yes) { - // Do nothing. - } else { - return; - } - } - } else { - // Do nothing; - if (pkgType.contains("PATCH") || category == "ac") { - QMessageBox::information( - this, tr("PKG Extraction"), - tr("PKG is a patch or DLC, please install the game first!")); - return; - } - // what else? - } - if (!pkg.Extract(file, game_update_path, failreason)) { - QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); - } else { - int nfiles = pkg.GetNumberOfFiles(); - - if (nfiles > 0) { - QVector indices; - for (int i = 0; i < nfiles; i++) { - indices.append(i); - } - - QProgressDialog dialog; - dialog.setWindowTitle(tr("PKG Extraction")); - dialog.setWindowModality(Qt::WindowModal); - QString extractmsg = QString(tr("Extracting PKG %1/%2")).arg(pkgNum).arg(nPkg); - dialog.setLabelText(extractmsg); - dialog.setAutoClose(true); - dialog.setRange(0, nfiles); - - dialog.setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, - dialog.size(), this->geometry())); - - QFutureWatcher futureWatcher; - connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { - if (pkgNum == nPkg) { - QString path; - - // We want to show the parent path instead of the full path - Common::FS::PathToQString(path, game_folder_path.parent_path()); - QIcon windowIcon( - Common::FS::PathToUTF8String(game_folder_path / "sce_sys/icon0.png") - .c_str()); - - QMessageBox extractMsgBox(this); - extractMsgBox.setWindowTitle(tr("Extraction Finished")); - if (!windowIcon.isNull()) { - extractMsgBox.setWindowIcon(windowIcon); - } - extractMsgBox.setText( - QString(tr("Game successfully installed at %1")).arg(path)); - extractMsgBox.addButton(QMessageBox::Ok); - extractMsgBox.setDefaultButton(QMessageBox::Ok); - connect(&extractMsgBox, &QMessageBox::buttonClicked, this, - [&](QAbstractButton* button) { - if (extractMsgBox.button(QMessageBox::Ok) == button) { - extractMsgBox.close(); - emit ExtractionFinished(); - } - }); - extractMsgBox.exec(); - } - if (delete_file_on_install) { - std::filesystem::remove(file); - } - }); - connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); }); - connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &dialog, - &QProgressDialog::setValue); - futureWatcher.setFuture( - QtConcurrent::map(indices, [&](int index) { pkg.ExtractFiles(index); })); - dialog.exec(); - } - } - } else { - QMessageBox::critical(this, tr("PKG ERROR"), - tr("File doesn't appear to be a valid PKG file")); - } -} - void MainWindow::InstallDirectory() { GameInstallDialog dlg; dlg.exec(); @@ -1340,7 +1060,6 @@ QIcon MainWindow::RecolorIcon(const QIcon& icon, bool isWhite) { } void MainWindow::SetUiIcons(bool isWhite) { - ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite)); ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite)); ui->shadFolderAct->setIcon(RecolorIcon(ui->shadFolderAct->icon(), isWhite)); ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite)); @@ -1368,7 +1087,6 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->icon(), isWhite)); ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite)); - ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite)); ui->trophyViewerAct->setIcon(RecolorIcon(ui->trophyViewerAct->icon(), isWhite)); ui->configureAct->setIcon(RecolorIcon(ui->configureAct->icon(), isWhite)); ui->addElfFolderAct->setIcon(RecolorIcon(ui->addElfFolderAct->icon(), isWhite)); diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index bcd5e53ba..5d05bfca4 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -22,7 +22,6 @@ #include "game_list_utils.h" #include "main_window_themes.h" #include "main_window_ui.h" -#include "pkg_viewer.h" class GameListFrame; @@ -36,7 +35,6 @@ public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow(); bool Init(); - void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg); void InstallDirectory(); void StartGame(); void PauseGame(); @@ -72,7 +70,6 @@ private: void SetLastUsedTheme(); void SetLastIconSizeBullet(); void SetUiIcons(bool isWhite); - void InstallPkg(); void BootGame(); void AddRecentFiles(QString filePath); void LoadTranslation(); @@ -89,7 +86,6 @@ private: QActionGroup* m_list_mode_act_group = nullptr; QActionGroup* m_theme_act_group = nullptr; QActionGroup* m_recent_files_group = nullptr; - PKG pkg; // Dockable widget frames WindowThemes m_window_themes; GameListUtils m_game_list_utils; @@ -120,20 +116,6 @@ protected: } } - void dropEvent(QDropEvent* event1) override { - const QMimeData* mimeData = event1->mimeData(); - if (mimeData->hasUrls()) { - QList urlList = mimeData->urls(); - int pkgNum = 0; - int nPkg = urlList.size(); - for (const QUrl& url : urlList) { - pkgNum++; - std::filesystem::path path = Common::FS::PathFromQString(url.toLocalFile()); - InstallDragDropPkg(path, pkgNum, nPkg); - } - } - } - void resizeEvent(QResizeEvent* event) override; std::filesystem::path last_install_dir = ""; diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index c4f47b636..2c4d4480b 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -9,7 +9,6 @@ class Ui_MainWindow { public: - QAction* bootInstallPkgAct; QAction* bootGameAct; QAction* addElfFolderAct; QAction* shadFolderAct; @@ -27,7 +26,6 @@ public: QAction* gameInstallPathAct; QAction* downloadCheatsPatchesAct; QAction* dumpGameListAct; - QAction* pkgViewerAct; QAction* trophyViewerAct; #ifdef ENABLE_UPDATER QAction* updaterAct; @@ -87,9 +85,6 @@ public: MainWindow->setDockNestingEnabled(true); MainWindow->setDockOptions(QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks | QMainWindow::AnimatedDocks | QMainWindow::GroupedDragging); - bootInstallPkgAct = new QAction(MainWindow); - bootInstallPkgAct->setObjectName("bootInstallPkgAct"); - bootInstallPkgAct->setIcon(QIcon(":images/file_icon.png")); bootGameAct = new QAction(MainWindow); bootGameAct->setObjectName("bootGameAct"); bootGameAct->setIcon(QIcon(":images/play_icon.png")); @@ -148,9 +143,6 @@ public: dumpGameListAct = new QAction(MainWindow); dumpGameListAct->setObjectName("dumpGameList"); dumpGameListAct->setIcon(QIcon(":images/dump_icon.png")); - pkgViewerAct = new QAction(MainWindow); - pkgViewerAct->setObjectName("pkgViewer"); - pkgViewerAct->setIcon(QIcon(":images/file_icon.png")); trophyViewerAct = new QAction(MainWindow); trophyViewerAct->setObjectName("trophyViewer"); trophyViewerAct->setIcon(QIcon(":images/trophy_icon.png")); @@ -309,7 +301,6 @@ public: menuBar->addAction(menuView->menuAction()); menuBar->addAction(menuSettings->menuAction()); menuBar->addAction(menuHelp->menuAction()); - menuFile->addAction(bootInstallPkgAct); menuFile->addAction(bootGameAct); menuFile->addSeparator(); menuFile->addAction(addElfFolderAct); @@ -345,7 +336,6 @@ public: menuSettings->addAction(menuUtils->menuAction()); menuUtils->addAction(downloadCheatsPatchesAct); menuUtils->addAction(dumpGameListAct); - menuUtils->addAction(pkgViewerAct); menuUtils->addAction(trophyViewerAct); #ifdef ENABLE_UPDATER menuHelp->addAction(updaterAct); @@ -361,8 +351,6 @@ public: MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "shadPS4", nullptr)); addElfFolderAct->setText( QCoreApplication::translate("MainWindow", "Open/Add Elf Folder", nullptr)); - bootInstallPkgAct->setText( - QCoreApplication::translate("MainWindow", "Install Packages (PKG)", nullptr)); bootGameAct->setText(QCoreApplication::translate("MainWindow", "Boot Game", nullptr)); #ifdef ENABLE_UPDATER updaterAct->setText( @@ -371,8 +359,6 @@ public: aboutAct->setText(QCoreApplication::translate("MainWindow", "About shadPS4", nullptr)); configureAct->setText(QCoreApplication::translate("MainWindow", "Configure...", nullptr)); #if QT_CONFIG(tooltip) - bootInstallPkgAct->setToolTip(QCoreApplication::translate( - "MainWindow", "Install application from a .pkg file", nullptr)); #endif // QT_CONFIG(tooltip) menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr)); shadFolderAct->setText( @@ -404,7 +390,6 @@ public: QCoreApplication::translate("MainWindow", "Download Cheats/Patches", nullptr)); dumpGameListAct->setText( QCoreApplication::translate("MainWindow", "Dump Game List", nullptr)); - pkgViewerAct->setText(QCoreApplication::translate("MainWindow", "PKG Viewer", nullptr)); trophyViewerAct->setText( QCoreApplication::translate("MainWindow", "Trophy Viewer", nullptr)); mw_searchbar->setPlaceholderText( @@ -433,4 +418,4 @@ public: namespace Ui { class MainWindow : public Ui_MainWindow {}; -} // namespace Ui \ No newline at end of file +} // namespace Ui diff --git a/src/qt_gui/pkg_viewer.cpp b/src/qt_gui/pkg_viewer.cpp deleted file mode 100644 index ecbc6312d..000000000 --- a/src/qt_gui/pkg_viewer.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "pkg_viewer.h" - -PKGViewer::PKGViewer(std::shared_ptr game_info_get, QWidget* parent, - std::function InstallDragDropPkg) - : QMainWindow(), m_game_info(game_info_get) { - this->resize(1280, 720); - this->setAttribute(Qt::WA_DeleteOnClose); - dir_list_std = Config::getPkgViewer(); - dir_list.clear(); - for (const auto& str : dir_list_std) { - dir_list.append(QString::fromStdString(str)); - } - statusBar = new QStatusBar(treeWidget); - this->setStatusBar(statusBar); - treeWidget = new QTreeWidget(this); - treeWidget->setColumnCount(9); - QStringList headers; - headers << tr("Name") << tr("Serial") << tr("Installed") << tr("Size") << tr("Category") - << tr("Type") << tr("App Ver") << tr("FW") << tr("Region") << tr("Flags") << tr("Path"); - treeWidget->setHeaderLabels(headers); - treeWidget->header()->setDefaultAlignment(Qt::AlignCenter); - treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); - treeWidget->setColumnWidth(8, 170); - this->setCentralWidget(treeWidget); - QMenuBar* menuBar = new QMenuBar(this); - menuBar->setContextMenuPolicy(Qt::PreventContextMenu); - QMenu* fileMenu = menuBar->addMenu(tr("File")); - QAction* openFolderAct = new QAction(tr("Open Folder"), this); - fileMenu->addAction(openFolderAct); - this->setMenuBar(menuBar); - CheckPKGFolders(); // Check for new PKG files in existing folders. - ProcessPKGInfo(); - - connect(openFolderAct, &QAction::triggered, this, &PKGViewer::OpenPKGFolder); - - connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, - [=, this](const QPoint& pos) { - if (treeWidget->selectedItems().isEmpty()) { - return; - } - m_gui_context_menus.RequestGameMenuPKGViewer(pos, m_full_pkg_list, treeWidget, - InstallDragDropPkg); - }); - - connect(parent, &QWidget::destroyed, this, [this]() { this->deleteLater(); }); -} - -PKGViewer::~PKGViewer() {} - -void PKGViewer::OpenPKGFolder() { - QString folderPath = - QFileDialog::getExistingDirectory(this, tr("Open Folder"), QDir::homePath()); - if (!dir_list.contains(folderPath)) { - dir_list.append(folderPath); - QDir directory(folderPath); - QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files); - for (const QFileInfo& fileInfo : fileInfoList) { - QString file_ext = fileInfo.suffix(); - if (fileInfo.isFile() && file_ext == "pkg") { - m_pkg_list.append(fileInfo.absoluteFilePath()); - } - } - std::sort(m_pkg_list.begin(), m_pkg_list.end()); - ProcessPKGInfo(); - dir_list_std.clear(); - for (auto dir : dir_list) { - dir_list_std.push_back(dir.toStdString()); - } - Config::setPkgViewer(dir_list_std); - } else { - // qDebug() << "Folder selection canceled."; - } -} - -void PKGViewer::CheckPKGFolders() { // Check for new PKG file additions. - m_pkg_list.clear(); - for (const QString& dir : dir_list) { - QDir directory(dir); - QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files); - for (const QFileInfo& fileInfo : fileInfoList) { - QString file_ext = fileInfo.suffix(); - if (fileInfo.isFile() && file_ext == "pkg") { - m_pkg_list.append(fileInfo.absoluteFilePath()); - } - } - } - std::sort(m_pkg_list.begin(), m_pkg_list.end()); -} - -void PKGViewer::ProcessPKGInfo() { - treeWidget->clear(); - map_strings.clear(); - map_integers.clear(); - m_pkg_app_list.clear(); - m_pkg_patch_list.clear(); - m_full_pkg_list.clear(); - for (int i = 0; i < m_pkg_list.size(); i++) { - std::filesystem::path path = Common::FS::PathFromQString(m_pkg_list[i]); - std::string failreason; - if (!package.Open(path, failreason)) { - QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); - return; - } - psf.Open(package.sfo); - QString title_name = QString::fromStdString( - std::string{psf.GetString("TITLE").value_or(std::string{tr("Unknown").toStdString()})}); - QString title_id = QString::fromStdString(std::string{ - psf.GetString("TITLE_ID").value_or(std::string{tr("Unknown").toStdString()})}); - QString app_type = GameListUtils::GetAppType(psf.GetInteger("APP_TYPE").value_or(0)); - QString app_version = QString::fromStdString(std::string{ - psf.GetString("APP_VER").value_or(std::string{tr("Unknown").toStdString()})}); - QString title_category = QString::fromStdString(std::string{ - psf.GetString("CATEGORY").value_or(std::string{tr("Unknown").toStdString()})}); - QString pkg_size = GameListUtils::FormatSize(package.GetPkgHeader().pkg_size); - pkg_content_flag = package.GetPkgHeader().pkg_content_flags; - QString flagss = ""; - for (const auto& flag : package.flagNames) { - if (package.isFlagSet(pkg_content_flag, flag.first)) { - if (!flagss.isEmpty()) - flagss += (", "); - flagss += QString::fromStdString(flag.second.data()); - } - } - - QString fw_ = tr("Unknown"); - if (const auto fw_int_opt = psf.GetInteger("SYSTEM_VER"); fw_int_opt.has_value()) { - const u32 fw_int = *fw_int_opt; - if (fw_int == 0) { - fw_ = "0.00"; - } else { - QString fw = QString::number(fw_int, 16); - fw_ = fw.length() > 7 ? QString::number(fw_int, 16).left(3).insert(2, '.') - : fw.left(3).insert(1, '.'); - } - } - char region = package.GetPkgHeader().pkg_content_id[0]; - QString pkg_info = ""; - if (title_category == "gd" && !flagss.contains("PATCH")) { - title_category = "App"; - pkg_info = title_name + ";;" + title_id + ";;" + pkg_size + ";;" + title_category + - ";;" + app_type + ";;" + app_version + ";;" + fw_ + ";;" + - game_list_util.GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i]; - m_pkg_app_list.append(pkg_info); - } else { - title_category = "Patch"; - pkg_info = title_name + ";;" + title_id + ";;" + pkg_size + ";;" + title_category + - ";;" + app_type + ";;" + app_version + ";;" + fw_ + ";;" + - game_list_util.GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i]; - m_pkg_patch_list.append(pkg_info); - } - } - std::sort(m_pkg_app_list.begin(), m_pkg_app_list.end()); - for (int i = 0; i < m_pkg_app_list.size(); i++) { - QTreeWidgetItem* treeItem = new QTreeWidgetItem(treeWidget); - QStringList pkg_app_ = m_pkg_app_list[i].split(";;"); - m_full_pkg_list.append(m_pkg_app_list[i]); - treeItem->setExpanded(true); - treeItem->setText(0, pkg_app_[0]); - treeItem->setText(1, pkg_app_[1]); - treeItem->setText(3, pkg_app_[2]); - treeItem->setTextAlignment(3, Qt::AlignCenter); - treeItem->setText(4, pkg_app_[3]); - treeItem->setTextAlignment(4, Qt::AlignCenter); - treeItem->setText(5, pkg_app_[4]); - treeItem->setTextAlignment(5, Qt::AlignCenter); - treeItem->setText(6, pkg_app_[5]); - treeItem->setTextAlignment(6, Qt::AlignCenter); - treeItem->setText(7, pkg_app_[6]); - treeItem->setTextAlignment(7, Qt::AlignCenter); - treeItem->setText(8, pkg_app_[7]); - treeItem->setTextAlignment(8, Qt::AlignCenter); - treeItem->setText(9, pkg_app_[8]); - treeItem->setText(10, pkg_app_[9]); - for (const GameInfo& info : m_game_info->m_games) { // Check if game is installed. - if (info.serial == pkg_app_[1].toStdString()) { - treeItem->setText(2, QChar(0x2713)); - treeItem->setTextAlignment(2, Qt::AlignCenter); - } - } - for (const QString& item : m_pkg_patch_list) { - QStringList pkg_patch_ = item.split(";;"); - if (pkg_patch_[1] == pkg_app_[1]) { // check patches with serial. - m_full_pkg_list.append(item); - QTreeWidgetItem* childItem = new QTreeWidgetItem(treeItem); - childItem->setText(0, pkg_patch_[0]); - childItem->setText(1, pkg_patch_[1]); - childItem->setText(3, pkg_patch_[2]); - childItem->setTextAlignment(3, Qt::AlignCenter); - childItem->setText(4, pkg_patch_[3]); - childItem->setTextAlignment(4, Qt::AlignCenter); - childItem->setText(5, pkg_patch_[4]); - childItem->setTextAlignment(5, Qt::AlignCenter); - childItem->setText(6, pkg_patch_[5]); - childItem->setTextAlignment(6, Qt::AlignCenter); - childItem->setText(7, pkg_patch_[6]); - childItem->setTextAlignment(7, Qt::AlignCenter); - childItem->setText(8, pkg_patch_[7]); - childItem->setTextAlignment(8, Qt::AlignCenter); - childItem->setText(9, pkg_patch_[8]); - childItem->setText(10, pkg_patch_[9]); - } - } - } - - for (int column = 0; column < treeWidget->columnCount() - 2; ++column) { - // Resize the column to fit its contents - treeWidget->resizeColumnToContents(column); - } - // Update status bar. - statusBar->clearMessage(); - int numPkgs = m_pkg_list.size(); - QString statusMessage = QString::number(numPkgs) + " " + tr("Package"); - statusBar->showMessage(statusMessage); -} \ No newline at end of file diff --git a/src/qt_gui/pkg_viewer.h b/src/qt_gui/pkg_viewer.h deleted file mode 100644 index 265a03b92..000000000 --- a/src/qt_gui/pkg_viewer.h +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include - -#include "common/io_file.h" -#include "core/file_format/pkg.h" -#include "core/file_format/pkg_type.h" -#include "core/file_format/psf.h" -#include "game_info.h" -#include "game_list_utils.h" -#include "gui_context_menus.h" - -class PKGViewer : public QMainWindow { - Q_OBJECT -public: - explicit PKGViewer( - std::shared_ptr game_info_get, QWidget* parent, - std::function InstallDragDropPkg = nullptr); - ~PKGViewer(); - void OpenPKGFolder(); - void CheckPKGFolders(); - void ProcessPKGInfo(); - -private: - GuiContextMenus m_gui_context_menus; - PKG package; - PSF psf; - PKGHeader pkgheader; - PKGEntry entry; - PSFHeader header; - char pkgTitleID[9]; - std::vector pkg; - u64 pkgSize = 0; - std::unordered_map map_strings; - std::unordered_map map_integers; - - u32_be pkg_content_flag; - std::shared_ptr m_game_info; - GameListUtils game_list_util; - // Status bar - QStatusBar* statusBar; - - std::vector> appTypes = { - {0, "FULL APP"}, - {1, "UPGRADABLE"}, - {2, "DEMO"}, - {3, "FREEMIUM"}, - }; - - QStringList m_full_pkg_list; - QStringList m_pkg_app_list; - QStringList m_pkg_patch_list; - QStringList m_pkg_list; - QStringList dir_list; - std::vector dir_list_std; - QTreeWidget* treeWidget = nullptr; -}; \ No newline at end of file From 31e1d4f839118b59398ca6f871929fc0e286e13c Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Fri, 28 Mar 2025 10:32:17 -0700 Subject: [PATCH 35/52] misc: Remove dead code. (#2702) --- CMakeLists.txt | 4 - documents/Quickstart/2.png | Bin 674148 -> 0 bytes documents/Quickstart/Quickstart.md | 9 +- documents/building-linux.md | 2 +- src/common/config.cpp | 12 - src/common/config.h | 2 - src/core/file_format/pkg.cpp | 473 ----------------------------- src/core/loader.cpp | 28 -- src/core/loader.h | 18 -- src/qt_gui/gui_context_menus.h | 25 -- src/qt_gui/install_dir_select.cpp | 94 ------ src/qt_gui/install_dir_select.h | 42 --- src/qt_gui/main_window.cpp | 2 - 13 files changed, 2 insertions(+), 709 deletions(-) delete mode 100644 documents/Quickstart/2.png delete mode 100644 src/core/file_format/pkg.cpp delete mode 100644 src/core/loader.cpp delete mode 100644 src/core/loader.h delete mode 100644 src/qt_gui/install_dir_select.cpp delete mode 100644 src/qt_gui/install_dir_select.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0359246c8..13204f479 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -678,8 +678,6 @@ set(CORE src/core/aerolib/stubs.cpp src/core/file_format/trp.h src/core/file_sys/fs.cpp src/core/file_sys/fs.h - src/core/loader.cpp - src/core/loader.h src/core/loader/dwarf.cpp src/core/loader/dwarf.h src/core/loader/elf.cpp @@ -969,8 +967,6 @@ 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/trophy_viewer.cpp src/qt_gui/trophy_viewer.h src/qt_gui/elf_viewer.cpp diff --git a/documents/Quickstart/2.png b/documents/Quickstart/2.png deleted file mode 100644 index 7e5bdfb155e572865777f38ef686257cedcc60de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 674148 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%0D&Jaj*t^~An4&Mk>ql6akxljo4M#GxIb)kg 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%0D`scYI^AhKK6L+2_lMh$J2X{*r%hQ#Zf)uO(QM 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 bYD)h<14Wu<5jwU300000NkvXXu0mjf4H#`p diff --git a/documents/Quickstart/Quickstart.md b/documents/Quickstart/Quickstart.md index 2f2751887..9c6bc5a6f 100644 --- a/documents/Quickstart/Quickstart.md +++ b/documents/Quickstart/Quickstart.md @@ -13,7 +13,6 @@ SPDX-License-Identifier: GPL-2.0-or-later - [**RAM**](#ram) - [**OS**](#os) - [**Have the latest WIP version**](#how-to-run-the-latest-work-in-progress-builds-of-shadps4) -- [**Install PKG files (Games and Updates)**](#install-pkg-files) - [**Configure the emulator**](#configure-the-emulator) ## Minimum PC requirements @@ -48,13 +47,7 @@ SPDX-License-Identifier: GPL-2.0-or-later 2. Once downloaded, extract to its own folder, and run shadPS4's executable from the extracted folder. -3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that shadPS4 can use to install your PKG files to. - -## Install PKG files - -To install PKG files (game and updates), you will need the Qt application (with UI). You will have to go to "File" then to "Install Packages (PKG)", a window will open then you will have to select the files. You can install multiple PKG files at once. Once finished, the game should appear in the application. - - +3. Upon first launch, shadPS4 will prompt you to select a folder to store your installed games in. Select "Browse" and then select a folder that contains your dumped games. ## Configure the emulator diff --git a/documents/building-linux.md b/documents/building-linux.md index 18ddab0c6..cdc8ba12f 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -108,7 +108,7 @@ Now run the emulator. If Qt was enabled at configure time: ./build/shadps4 ``` -Otherwise, specify the path to your PKG's boot file: +Otherwise, specify the path to your game's boot file: ```bash ./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin diff --git a/src/common/config.cpp b/src/common/config.cpp index b113ac0ef..8ead58686 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -98,7 +98,6 @@ u32 m_slider_pos_grid = 0; u32 m_table_mode = 0; u32 m_window_size_W = 1280; u32 m_window_size_H = 720; -std::vector m_pkg_viewer; std::vector m_elf_viewer; std::vector m_recent_files; std::string emulator_language = "en_US"; @@ -601,11 +600,6 @@ void setMainWindowHeight(u32 height) { m_window_size_H = height; } -void setPkgViewer(const std::vector& pkgList) { - m_pkg_viewer.resize(pkgList.size()); - m_pkg_viewer = pkgList; -} - void setElfViewer(const std::vector& elfList) { m_elf_viewer.resize(elfList.size()); m_elf_viewer = elfList; @@ -709,10 +703,6 @@ u32 getMainWindowHeight() { return m_window_size_H; } -std::vector getPkgViewer() { - return m_pkg_viewer; -} - std::vector getElfViewer() { return m_elf_viewer; } @@ -886,7 +876,6 @@ void load(const std::filesystem::path& path) { main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); main_window_geometry_w = toml::find_or(gui, "geometry_w", 0); main_window_geometry_h = toml::find_or(gui, "geometry_h", 0); - m_pkg_viewer = toml::find_or>(gui, "pkgDirs", {}); m_elf_viewer = toml::find_or>(gui, "elfDirs", {}); m_recent_files = toml::find_or>(gui, "recentFiles", {}); m_table_mode = toml::find_or(gui, "gameTableMode", 0); @@ -1105,7 +1094,6 @@ void saveMainWindow(const std::filesystem::path& path) { data["GUI"]["geometry_y"] = main_window_geometry_y; data["GUI"]["geometry_w"] = main_window_geometry_w; data["GUI"]["geometry_h"] = main_window_geometry_h; - data["GUI"]["pkgDirs"] = m_pkg_viewer; data["GUI"]["elfDirs"] = m_elf_viewer; data["GUI"]["recentFiles"] = m_recent_files; diff --git a/src/common/config.h b/src/common/config.h index 3a0bf252c..d040aa337 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -154,7 +154,6 @@ void setSliderPositionGrid(u32 pos); void setTableMode(u32 mode); void setMainWindowWidth(u32 width); void setMainWindowHeight(u32 height); -void setPkgViewer(const std::vector& pkgList); void setElfViewer(const std::vector& elfList); void setRecentFiles(const std::vector& recentFiles); void setEmulatorLanguage(std::string language); @@ -174,7 +173,6 @@ u32 getSliderPositionGrid(); u32 getTableMode(); u32 getMainWindowWidth(); u32 getMainWindowHeight(); -std::vector getPkgViewer(); std::vector getElfViewer(); std::vector getRecentFiles(); std::string getEmulatorLanguage(); diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp deleted file mode 100644 index ecc5f10a4..000000000 --- a/src/core/file_format/pkg.cpp +++ /dev/null @@ -1,473 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include "common/io_file.h" -#include "common/logging/formatter.h" -#include "core/file_format/pkg.h" -#include "core/file_format/pkg_type.h" - -static void DecompressPFSC(std::span compressed_data, std::span decompressed_data) { - z_stream decompressStream; - decompressStream.zalloc = Z_NULL; - decompressStream.zfree = Z_NULL; - decompressStream.opaque = Z_NULL; - - if (inflateInit(&decompressStream) != Z_OK) { - // std::cerr << "Error initializing zlib for deflation." << std::endl; - } - - decompressStream.avail_in = compressed_data.size(); - decompressStream.next_in = reinterpret_cast(compressed_data.data()); - decompressStream.avail_out = decompressed_data.size(); - decompressStream.next_out = reinterpret_cast(decompressed_data.data()); - - if (inflate(&decompressStream, Z_FINISH)) { - } - if (inflateEnd(&decompressStream) != Z_OK) { - // std::cerr << "Error ending zlib inflate" << std::endl; - } -} - -u32 GetPFSCOffset(std::span pfs_image) { - static constexpr u32 PfscMagic = 0x43534650; - u32 value; - for (u32 i = 0x20000; i < pfs_image.size(); i += 0x10000) { - std::memcpy(&value, &pfs_image[i], sizeof(u32)); - if (value == PfscMagic) - return i; - } - return -1; -} - -PKG::PKG() = default; - -PKG::~PKG() = default; - -bool PKG::Open(const std::filesystem::path& filepath, std::string& failreason) { - Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); - if (!file.IsOpen()) { - return false; - } - pkgSize = file.GetSize(); - - file.Read(pkgheader); - if (pkgheader.magic != 0x7F434E54) - return false; - - for (const auto& flag : flagNames) { - if (isFlagSet(pkgheader.pkg_content_flags, flag.first)) { - if (!pkgFlags.empty()) - pkgFlags += (", "); - pkgFlags += (flag.second); - } - } - - // Find title id it is part of pkg_content_id starting at offset 0x40 - file.Seek(0x47); // skip first 7 characters of content_id - file.Read(pkgTitleID); - - u32 offset = pkgheader.pkg_table_entry_offset; - u32 n_files = pkgheader.pkg_table_entry_count; - - if (!file.Seek(offset)) { - failreason = "Failed to seek to PKG table entry offset"; - return false; - } - - for (int i = 0; i < n_files; i++) { - PKGEntry entry{}; - file.Read(entry.id); - file.Read(entry.filename_offset); - file.Read(entry.flags1); - file.Read(entry.flags2); - file.Read(entry.offset); - file.Read(entry.size); - file.Seek(8, Common::FS::SeekOrigin::CurrentPosition); - - // Try to figure out the name - const auto name = GetEntryNameByType(entry.id); - if (name == "param.sfo") { - sfo.clear(); - if (!file.Seek(entry.offset)) { - failreason = "Failed to seek to param.sfo offset"; - return false; - } - sfo.resize(entry.size); - file.ReadRaw(sfo.data(), entry.size); - } - } - file.Close(); - - return true; -} - -bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem::path& extract, - std::string& failreason) { - extract_path = extract; - pkgpath = filepath; - Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); - if (!file.IsOpen()) { - return false; - } - pkgSize = file.GetSize(); - file.ReadRaw(&pkgheader, sizeof(PKGHeader)); - - if (pkgheader.magic != 0x7F434E54) - return false; - - if (pkgheader.pkg_size > pkgSize) { - failreason = "PKG file size is different"; - return false; - } - if ((pkgheader.pkg_content_size + pkgheader.pkg_content_offset) > pkgheader.pkg_size) { - failreason = "Content size is bigger than pkg size"; - return false; - } - - u32 offset = pkgheader.pkg_table_entry_offset; - u32 n_files = pkgheader.pkg_table_entry_count; - - std::array concatenated_ivkey_dk3; - std::array seed_digest; - std::array, 7> digest1; - std::array, 7> key1; - std::array imgkeydata; - - if (!file.Seek(offset)) { - failreason = "Failed to seek to PKG table entry offset"; - return false; - } - - for (int i = 0; i < n_files; i++) { - PKGEntry entry{}; - file.Read(entry.id); - file.Read(entry.filename_offset); - file.Read(entry.flags1); - file.Read(entry.flags2); - file.Read(entry.offset); - file.Read(entry.size); - file.Seek(8, Common::FS::SeekOrigin::CurrentPosition); - - auto currentPos = file.Tell(); - - // Try to figure out the name - const auto name = GetEntryNameByType(entry.id); - const auto filepath = extract_path / "sce_sys" / name; - std::filesystem::create_directories(filepath.parent_path()); - - if (name.empty()) { - // Just print with id - Common::FS::IOFile out(extract_path / "sce_sys" / std::to_string(entry.id), - Common::FS::FileAccessMode::Write); - if (!file.Seek(entry.offset)) { - failreason = "Failed to seek to PKG entry offset"; - return false; - } - - std::vector data; - data.resize(entry.size); - file.ReadRaw(data.data(), entry.size); - out.WriteRaw(data.data(), entry.size); - out.Close(); - - file.Seek(currentPos); - continue; - } - - if (entry.id == 0x1) { // DIGESTS, seek; - // file.Seek(entry.offset, fsSeekSet); - } else if (entry.id == 0x10) { // ENTRY_KEYS, seek; - file.Seek(entry.offset); - file.Read(seed_digest); - - for (int i = 0; i < 7; i++) { - file.Read(digest1[i]); - } - - for (int i = 0; i < 7; i++) { - file.Read(key1[i]); - } - - PKG::crypto.RSA2048Decrypt(dk3_, key1[3], true); // decrypt DK3 - } else if (entry.id == 0x20) { // IMAGE_KEY, seek; IV_KEY - file.Seek(entry.offset); - file.Read(imgkeydata); - - // The Concatenated iv + dk3 imagekey for HASH256 - std::memcpy(concatenated_ivkey_dk3.data(), &entry, sizeof(entry)); - std::memcpy(concatenated_ivkey_dk3.data() + sizeof(entry), dk3_.data(), sizeof(dk3_)); - - PKG::crypto.ivKeyHASH256(concatenated_ivkey_dk3, ivKey); // ivkey_ - // imgkey_ to use for last step to get ekpfs - PKG::crypto.aesCbcCfb128Decrypt(ivKey, imgkeydata, imgKey); - // ekpfs key to get data and tweak keys. - PKG::crypto.RSA2048Decrypt(ekpfsKey, imgKey, false); - } else if (entry.id == 0x80) { - // GENERAL_DIGESTS, seek; - // file.Seek(entry.offset, fsSeekSet); - } - - Common::FS::IOFile out(extract_path / "sce_sys" / name, Common::FS::FileAccessMode::Write); - if (!file.Seek(entry.offset)) { - failreason = "Failed to seek to PKG entry offset"; - return false; - } - - std::vector data; - data.resize(entry.size); - file.ReadRaw(data.data(), entry.size); - out.WriteRaw(data.data(), entry.size); - out.Close(); - - // Decrypt Np stuff and overwrite. - if (entry.id == 0x400 || entry.id == 0x401 || entry.id == 0x402 || - entry.id == 0x403) { // somehow 0x401 is not decrypting - decNp.resize(entry.size); - if (!file.Seek(entry.offset)) { - failreason = "Failed to seek to PKG entry offset"; - return false; - } - - std::vector data; - data.resize(entry.size); - file.ReadRaw(data.data(), entry.size); - - std::span cipherNp(data.data(), entry.size); - std::array concatenated_ivkey_dk3_; - std::memcpy(concatenated_ivkey_dk3_.data(), &entry, sizeof(entry)); - std::memcpy(concatenated_ivkey_dk3_.data() + sizeof(entry), dk3_.data(), sizeof(dk3_)); - PKG::crypto.ivKeyHASH256(concatenated_ivkey_dk3_, ivKey); - PKG::crypto.aesCbcCfb128DecryptEntry(ivKey, cipherNp, decNp); - - Common::FS::IOFile out(extract_path / "sce_sys" / name, - Common::FS::FileAccessMode::Write); - out.Write(decNp); - out.Close(); - } - - file.Seek(currentPos); - } - - // Read the seed - std::array seed; - if (!file.Seek(pkgheader.pfs_image_offset + 0x370)) { - failreason = "Failed to seek to PFS image offset"; - return false; - } - file.Read(seed); - - // Get data and tweak keys. - PKG::crypto.PfsGenCryptoKey(ekpfsKey, seed, dataKey, tweakKey); - const u32 length = pkgheader.pfs_cache_size * 0x2; // Seems to be ok. - - int num_blocks = 0; - std::vector pfsc(length); - if (length != 0) { - // Read encrypted pfs_image - std::vector pfs_encrypted(length); - file.Seek(pkgheader.pfs_image_offset); - file.Read(pfs_encrypted); - file.Close(); - // Decrypt the pfs_image. - std::vector pfs_decrypted(length); - PKG::crypto.decryptPFS(dataKey, tweakKey, pfs_encrypted, pfs_decrypted, 0); - - // Retrieve PFSC from decrypted pfs_image. - pfsc_offset = GetPFSCOffset(pfs_decrypted); - std::memcpy(pfsc.data(), pfs_decrypted.data() + pfsc_offset, length - pfsc_offset); - - PFSCHdr pfsChdr; - std::memcpy(&pfsChdr, pfsc.data(), sizeof(pfsChdr)); - - num_blocks = (int)(pfsChdr.data_length / pfsChdr.block_sz2); - sectorMap.resize(num_blocks + 1); // 8 bytes, need extra 1 to get the last offset. - - for (int i = 0; i < num_blocks + 1; i++) { - std::memcpy(§orMap[i], pfsc.data() + pfsChdr.block_offsets + i * 8, 8); - } - } - - u32 ent_size = 0; - u32 ndinode = 0; - int ndinode_counter = 0; - bool dinode_reached = false; - bool uroot_reached = false; - std::vector compressedData; - std::vector decompressedData(0x10000); - - // Get iNdoes and Dirents. - for (int i = 0; i < num_blocks; i++) { - const u64 sectorOffset = sectorMap[i]; - const u64 sectorSize = sectorMap[i + 1] - sectorOffset; - - compressedData.resize(sectorSize); - std::memcpy(compressedData.data(), pfsc.data() + sectorOffset, sectorSize); - - if (sectorSize == 0x10000) // Uncompressed data - std::memcpy(decompressedData.data(), compressedData.data(), 0x10000); - else if (sectorSize < 0x10000) // Compressed data - DecompressPFSC(compressedData, decompressedData); - - if (i == 0) { - std::memcpy(&ndinode, decompressedData.data() + 0x30, 4); // number of folders and files - } - - int occupied_blocks = - (ndinode * 0xA8) / 0x10000; // how many blocks(0x10000) are taken by iNodes. - if (((ndinode * 0xA8) % 0x10000) != 0) - occupied_blocks += 1; - - if (i >= 1 && i <= occupied_blocks) { // Get all iNodes, gives type, file size and location. - for (int p = 0; p < 0x10000; p += 0xA8) { - Inode node; - std::memcpy(&node, &decompressedData[p], sizeof(node)); - if (node.Mode == 0) { - break; - } - iNodeBuf.push_back(node); - } - } - - // let's deal with the root/uroot entries here. - // Sometimes it's more than 2 entries (Tomb Raider Remastered) - const std::string_view flat_path_table(&decompressedData[0x10], 15); - if (flat_path_table == "flat_path_table") { - uroot_reached = true; - } - - if (uroot_reached) { - for (int i = 0; i < 0x10000; i += ent_size) { - Dirent dirent; - std::memcpy(&dirent, &decompressedData[i], sizeof(dirent)); - ent_size = dirent.entsize; - if (dirent.ino != 0) { - ndinode_counter++; - } else { - // Set the the folder according to the current inode. - // Can be 2 or more (rarely) - auto parent_path = extract_path.parent_path(); - auto title_id = GetTitleID(); - - if (parent_path.filename() != title_id && - !fmt::UTF(extract_path.u8string()).data.ends_with("-patch")) { - extractPaths[ndinode_counter] = parent_path / title_id; - } else { - // DLCs path has different structure - extractPaths[ndinode_counter] = extract_path; - } - uroot_reached = false; - break; - } - } - } - - const char dot = decompressedData[0x10]; - const std::string_view dotdot(&decompressedData[0x28], 2); - if (dot == '.' && dotdot == "..") { - dinode_reached = true; - } - - // Get folder and file names. - bool end_reached = false; - if (dinode_reached) { - for (int j = 0; j < 0x10000; j += ent_size) { // Skip the first parent and child. - Dirent dirent; - std::memcpy(&dirent, &decompressedData[j], sizeof(dirent)); - - // Stop here and continue the main loop - if (dirent.ino == 0) { - break; - } - - ent_size = dirent.entsize; - auto& table = fsTable.emplace_back(); - table.name = std::string(dirent.name, dirent.namelen); - table.inode = dirent.ino; - table.type = dirent.type; - - if (table.type == PFS_CURRENT_DIR) { - current_dir = extractPaths[table.inode]; - } - extractPaths[table.inode] = current_dir / std::filesystem::path(table.name); - - if (table.type == PFS_FILE || table.type == PFS_DIR) { - if (table.type == PFS_DIR) { // Create dirs. - std::filesystem::create_directory(extractPaths[table.inode]); - } - ndinode_counter++; - if ((ndinode_counter + 1) == ndinode) // 1 for the image itself (root). - end_reached = true; - } - } - if (end_reached) { - break; - } - } - } - return true; -} - -void PKG::ExtractFiles(const int index) { - int inode_number = fsTable[index].inode; - int inode_type = fsTable[index].type; - std::string inode_name = fsTable[index].name; - - if (inode_type == PFS_FILE) { - int sector_loc = iNodeBuf[inode_number].loc; - int nblocks = iNodeBuf[inode_number].Blocks; - int bsize = iNodeBuf[inode_number].Size; - - Common::FS::IOFile inflated; - inflated.Open(extractPaths[inode_number], Common::FS::FileAccessMode::Write); - - Common::FS::IOFile pkgFile; // Open the file for each iteration to avoid conflict. - pkgFile.Open(pkgpath, Common::FS::FileAccessMode::Read); - - int size_decompressed = 0; - std::vector compressedData; - std::vector decompressedData(0x10000); - - u64 pfsc_buf_size = 0x11000; // extra 0x1000 - std::vector pfsc(pfsc_buf_size); - std::vector pfs_decrypted(pfsc_buf_size); - - for (int j = 0; j < nblocks; j++) { - u64 sectorOffset = - sectorMap[sector_loc + j]; // offset into PFSC_image and not pfs_image. - u64 sectorSize = sectorMap[sector_loc + j + 1] - - sectorOffset; // indicates if data is compressed or not. - u64 fileOffset = (pkgheader.pfs_image_offset + pfsc_offset + sectorOffset); - u64 currentSector1 = - (pfsc_offset + sectorOffset) / 0x1000; // block size is 0x1000 for xts decryption. - - int sectorOffsetMask = (sectorOffset + pfsc_offset) & 0xFFFFF000; - int previousData = (sectorOffset + pfsc_offset) - sectorOffsetMask; - - pkgFile.Seek(fileOffset - previousData); - pkgFile.Read(pfsc); - - PKG::crypto.decryptPFS(dataKey, tweakKey, pfsc, pfs_decrypted, currentSector1); - - compressedData.resize(sectorSize); - std::memcpy(compressedData.data(), pfs_decrypted.data() + previousData, sectorSize); - - if (sectorSize == 0x10000) // Uncompressed data - std::memcpy(decompressedData.data(), compressedData.data(), 0x10000); - else if (sectorSize < 0x10000) // Compressed data - DecompressPFSC(compressedData, decompressedData); - - size_decompressed += 0x10000; - - if (j < nblocks - 1) { - inflated.WriteRaw(decompressedData.data(), decompressedData.size()); - } else { - // This is to remove the zeros at the end of the file. - const u32 write_size = decompressedData.size() - (size_decompressed - bsize); - inflated.WriteRaw(decompressedData.data(), write_size); - } - } - pkgFile.Close(); - inflated.Close(); - } -} diff --git a/src/core/loader.cpp b/src/core/loader.cpp deleted file mode 100644 index f80bfbb81..000000000 --- a/src/core/loader.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/io_file.h" -#include "common/types.h" -#include "loader.h" - -namespace Loader { - -FileTypes DetectFileType(const std::filesystem::path& filepath) { - // No file loaded - if (filepath.empty()) { - return FileTypes::Unknown; - } - Common::FS::IOFile file; - file.Open(filepath, Common::FS::FileAccessMode::Read); - file.Seek(0); - u32 magic; - file.Read(magic); - file.Close(); - switch (magic) { - case PkgMagic: - return FileTypes::Pkg; - } - return FileTypes::Unknown; -} - -} // namespace Loader diff --git a/src/core/loader.h b/src/core/loader.h deleted file mode 100644 index 608970dca..000000000 --- a/src/core/loader.h +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -namespace Loader { - -constexpr static u32 PkgMagic = 0x544e437f; - -enum class FileTypes { - Unknown, - Pkg, -}; - -FileTypes DetectFileType(const std::filesystem::path& filepath); -} // namespace Loader diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 0cc0e48dc..7dcb006ba 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -7,7 +7,6 @@ #include #include #include -#include #include #include @@ -599,30 +598,6 @@ public: return -1; } - void RequestGameMenuPKGViewer( - const QPoint& pos, QStringList m_pkg_app_list, QTreeWidget* treeWidget, - std::function InstallDragDropPkg) { - QPoint global_pos = treeWidget->viewport()->mapToGlobal(pos); // context menu position - QTreeWidgetItem* currentItem = treeWidget->currentItem(); // current clicked item - int itemIndex = GetRowIndex(treeWidget, currentItem); // row - - QMenu menu(treeWidget); - QAction installPackage(tr("Install PKG"), treeWidget); - - menu.addAction(&installPackage); - - auto selected = menu.exec(global_pos); - if (!selected) { - return; - } - - if (selected == &installPackage) { - QStringList pkg_app_ = m_pkg_app_list[itemIndex].split(";;"); - std::filesystem::path path = Common::FS::PathFromQString(pkg_app_[9]); - InstallDragDropPkg(path, 1, 1); - } - } - private: bool convertPngToIco(const QString& pngFilePath, const QString& icoFilePath) { // Load the PNG image diff --git a/src/qt_gui/install_dir_select.cpp b/src/qt_gui/install_dir_select.cpp deleted file mode 100644 index e90a10ee6..000000000 --- a/src/qt_gui/install_dir_select.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// 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 - -#include "install_dir_select.h" - -InstallDirSelect::InstallDirSelect() : selected_dir() { - auto install_dirs = Config::getGameInstallDirs(); - selected_dir = install_dirs.empty() ? "" : install_dirs.front(); - - if (!install_dirs.empty() && install_dirs.size() == 1) { - accept(); - } - - 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); - - auto checkbox = new QCheckBox(tr("Install All Queued to Selected Folder")); - connect(checkbox, &QCheckBox::toggled, this, &InstallDirSelect::setUseForAllQueued); - vlayout->addWidget(checkbox); - - auto checkbox2 = new QCheckBox(tr("Delete PKG File on Install")); - connect(checkbox2, &QCheckBox::toggled, this, &InstallDirSelect::setDeleteFileOnInstall); - vlayout->addWidget(checkbox2); - - 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; - } - } -} - -void InstallDirSelect::setUseForAllQueued(bool enabled) { - use_for_all_queued = enabled; -} - -void InstallDirSelect::setDeleteFileOnInstall(bool enabled) { - delete_file_on_install = enabled; -} - -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 deleted file mode 100644 index e11cbf381..000000000 --- a/src/qt_gui/install_dir_select.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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 { - Q_OBJECT - -public: - InstallDirSelect(); - ~InstallDirSelect(); - - std::filesystem::path getSelectedDirectory() { - return selected_dir; - } - - bool useForAllQueued() { - return use_for_all_queued; - } - - bool deleteFileOnInstall() { - return delete_file_on_install; - } - -private: - QWidget* SetupInstallDirList(); - QWidget* SetupDialogActions(); - void setSelectedDirectory(QListWidgetItem* item); - void setDeleteFileOnInstall(bool enabled); - void setUseForAllQueued(bool enabled); - std::filesystem::path selected_dir; - bool delete_file_on_install = false; - bool use_for_all_queued = false; -}; diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index e92676c02..5d8f8e717 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -20,9 +20,7 @@ #include "common/string_util.h" #include "common/version.h" #include "control_settings.h" -#include "core/loader.h" #include "game_install_dialog.h" -#include "install_dir_select.h" #include "kbm_gui.h" #include "main_window.h" #include "settings_dialog.h" From 501f46e51518a70db4d394ebe0b1510f1e165179 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 28 Mar 2025 19:48:23 +0200 Subject: [PATCH 36/52] New Crowdin updates (#2695) * New translations en_us.ts (Albanian) * New translations en_us.ts (Polish) * New translations en_us.ts (Spanish) * New translations en_us.ts (Romanian) * New translations en_us.ts (French) * New translations en_us.ts (Arabic) * New translations en_us.ts (Danish) * New translations en_us.ts (German) * New translations en_us.ts (Greek) * New translations en_us.ts (Finnish) * New translations en_us.ts (Hungarian) * New translations en_us.ts (Italian) * New translations en_us.ts (Japanese) * New translations en_us.ts (Korean) * New translations en_us.ts (Lithuanian) * New translations en_us.ts (Dutch) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Russian) * New translations en_us.ts (Swedish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Turkish) * New translations en_us.ts (Ukrainian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Chinese Traditional) * New translations en_us.ts (Vietnamese) * New translations en_us.ts (Indonesian) * New translations en_us.ts (Persian) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Arabic) * New translations en_us.ts (Turkish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Russian) * New translations en_us.ts (Finnish) * New translations en_us.ts (Finnish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Arabic) --- src/qt_gui/translations/ar_SA.ts | 42 ++++++++- src/qt_gui/translations/da_DK.ts | 40 ++++++++ src/qt_gui/translations/de_DE.ts | 40 ++++++++ src/qt_gui/translations/el_GR.ts | 40 ++++++++ src/qt_gui/translations/es_ES.ts | 40 ++++++++ src/qt_gui/translations/fa_IR.ts | 40 ++++++++ src/qt_gui/translations/fi_FI.ts | 156 +++++++++++++++++++------------ src/qt_gui/translations/fr_FR.ts | 40 ++++++++ src/qt_gui/translations/hu_HU.ts | 40 ++++++++ src/qt_gui/translations/id_ID.ts | 40 ++++++++ src/qt_gui/translations/it_IT.ts | 46 ++++++++- src/qt_gui/translations/ja_JP.ts | 40 ++++++++ src/qt_gui/translations/ko_KR.ts | 40 ++++++++ src/qt_gui/translations/lt_LT.ts | 40 ++++++++ src/qt_gui/translations/nb_NO.ts | 40 ++++++++ src/qt_gui/translations/nl_NL.ts | 40 ++++++++ src/qt_gui/translations/pl_PL.ts | 40 ++++++++ src/qt_gui/translations/pt_BR.ts | 42 ++++++++- src/qt_gui/translations/pt_PT.ts | 40 ++++++++ src/qt_gui/translations/ro_RO.ts | 40 ++++++++ src/qt_gui/translations/ru_RU.ts | 40 ++++++++ src/qt_gui/translations/sq_AL.ts | 40 ++++++++ src/qt_gui/translations/sv_SE.ts | 40 ++++++++ src/qt_gui/translations/tr_TR.ts | 40 ++++++++ src/qt_gui/translations/uk_UA.ts | 40 ++++++++ src/qt_gui/translations/vi_VN.ts | 40 ++++++++ src/qt_gui/translations/zh_CN.ts | 40 ++++++++ src/qt_gui/translations/zh_TW.ts | 40 ++++++++ 28 files changed, 1183 insertions(+), 63 deletions(-) diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index ac6920ea0..9808fdbe6 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -1149,7 +1149,7 @@ Deadzone Offset (def 0.50): - + Deadzone Offset (def 0.50): Speed Multiplier (def 1.0): @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + أبدأ اللعب + + + Pause + توقف مؤقت + + + Stop + إيقاف + + + Restart + إعادة تشغيل + + + Full Screen + وضع ملء الشاشة + + + Controllers + أذرعة التحكم + + + Keyboard + لوحة المفاتيح + + + Refresh List + تحديث القائمة + + + Resume + استئناف + + + Show Labels Under Icons + إظهار العلامات أسفل الأيقونات + PKGViewer diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 1835ba84c..1547a0e13 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index 6717a93ef..c0e43065b 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index 6e1adaac9..e6fa989aa 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 24844d0a2..288c445c3 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 6b7af042e..1b8813a80 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1514,6 +1514,46 @@ shadPS4 ShadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index 324cb6c49..cd880fb23 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -407,206 +407,206 @@ ControlSettings Configure Controls - Configure Controls + Määritä Kontrollit D-Pad - D-Pad + D-Pad Up - Up + Ylös Left - Left + Vasen Right - Right + Oikea Down - Down + Alas Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Vasemman Analogin Deadzone (oletus:2 max:127) Left Deadzone - Left Deadzone + Vasen Deadzone Left Stick - Left Stick + Vasen Analogi Config Selection - Config Selection + Asetusten Valinta Common Config - Common Config + Yleinen Asetus Use per-game configs - Use per-game configs + Käytä pelikohtaisia asetuksia L1 / LB - L1 / LB + L1 / LB L2 / LT - L2 / LT + L2 / LT Back - Back + Back R1 / RB - R1 / RB + R1 / RB R2 / RT - R2 / RT + R2 / RT L3 - L3 + L3 Options / Start - Options / Start + Options / Start R3 - R3 + R3 Face Buttons - Face Buttons + Etunäppäimet Triangle / Y - Triangle / Y + Kolmio / Y Square / X - Square / X + Neliö / X Circle / B - Circle / B + Ympyrä / B Cross / A - Cross / A + Rasti / A Right Stick Deadzone (def:2, max:127) - Right Stick Deadzone (def:2, max:127) + Oikean Analogin Deadzone (oletus:2 max:127) Right Deadzone - Right Deadzone + Oikea Deadzone Right Stick - Right Stick + Oikea Analogi Color Adjustment - Color Adjustment + Värinhallinta R: - R: + R: G: - G: + G: B: - B: + B: Override Lightbar Color - Override Lightbar Color + Pakota Ohjaimen Valopalkin Väri Override Color - Override Color + Pakotettava Väri Unable to Save - Unable to Save + Tallentaminen Epäonnistui Cannot bind axis values more than once - Cannot bind axis values more than once + Akseliarvoja ei voi määrittää kertaa useammin Save - Save + Tallenna Apply - Apply + Ota Käyttöön Restore Defaults - Restore Defaults + Palauta Oletukset Cancel - Cancel + Peruuta EditorDialog Edit Keyboard + Mouse and Controller input bindings - Edit Keyboard + Mouse and Controller input bindings + Muokkaa Näppäimistön + Hiiren ja Ohjaimen näppäinasetuksia Use Per-Game configs - Use Per-Game configs + Käytä Pelikohtaisia Asetuksia Error - Error + Virhe Could not open the file for reading - Could not open the file for reading + Tiedostoa ei voitu avata luettavaksi Could not open the file for writing - Could not open the file for writing + Tiedostoa ei voitu avata kirjoitettavaksi Save Changes - Save Changes + Tallenna Muutokset Do you want to save changes? - Do you want to save changes? + Haluatko tallentaa muutokset? Help - Help + Tietoa Do you want to reset your custom default config to the original default config? - Do you want to reset your custom default config to the original default config? + Haluatko nollata oletusasetuksiin tekemäsi muutokset? Do you want to reset this config to your custom default config? - Do you want to reset this config to your custom default config? + Haluato palauttaa nämä asetukset takaisin määrittämiisi oletuksiin? Reset to Default @@ -1077,35 +1077,35 @@ L3 - L3 + L3 Touchpad Click - Touchpad Click + Kosketuslevyn Klikkaus Mouse to Joystick - Mouse to Joystick + Hiiri Joystickinä *press F7 ingame to activate - *press F7 ingame to activate + *paina F7 pelissä aktivoidaksesi R3 - R3 + R3 Options - Options + Options Mouse Movement Parameters - Mouse Movement Parameters + Hiiren Liikkeen Parametrit note: click Help Button/Special Keybindings for more information - note: click Help Button/Special Keybindings for more information + huomio: klikkaa apunappia/näppäintä saadaksesi lisää tietoa Face Buttons @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index ec3f9f8b5..1f7a726cd 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 6672337a6..8746058b3 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index e43d31976..5960715ae 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index e63da05b8..3e95e3df6 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -1308,11 +1308,11 @@ Trophy Viewer - Trophy Viewer + Visualizzatore Trofei No games found. Please add your games to your library first. - No games found. Please add your games to your library first. + Nessun gioco trovato. Aggiungi prima i tuoi giochi alla tua libreria. PKG Viewer @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer @@ -2201,7 +2241,7 @@ Select Game: - Select Game: + Seleziona Gioco: Progress diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 7cf9fc5c2..1aaa3fa7c 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index d5289ace9..9dd06028d 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 17133da35..6e98ddc45 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index e8ce99f90..6faff415e 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index 5c1725bd5..376eea5ef 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 033412efa..a77b43e09 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index d44efce5d..ea82086f3 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -912,7 +912,7 @@ Are you sure you want to delete %1's %2 directory? - Tem certeza de que deseja excluir o diretório %2 de %1? + Tem certeza de que deseja excluir o diretório do %2 %1? Open Update Folder @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Jogar + + + Pause + Pausar + + + Stop + Parar + + + Restart + Reiniciar + + + Full Screen + Tela Cheia + + + Controllers + Controles + + + Keyboard + Teclado + + + Refresh List + Atualizar Lista + + + Resume + Continuar + + + Show Labels Under Icons + Mostrar Rótulos Sob Ícones + PKGViewer diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index 455955fad..7ca3eebb5 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 9c7720e17..6e008ac20 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 68eaabc34..560c8c110 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Играть + + + Pause + Пауза + + + Stop + Остановить + + + Restart + Перезапустить + + + Full Screen + Полный экран + + + Controllers + Контроллеры + + + Keyboard + Клавиатура + + + Refresh List + Обновить список + + + Resume + Продолжить + + + Show Labels Under Icons + Показывать метки под значками + PKGViewer diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index 657f78d0d..8a2c34c60 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index a002b150a..91b544e05 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 48ce4254d..4946874c9 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Başlat + + + Pause + Duraklat + + + Stop + Durdur + + + Restart + Yeniden Başlat + + + Full Screen + Tam Ekran + + + Controllers + Kontrolcüler + + + Keyboard + Klavye + + + Refresh List + Listeyi Yenile + + + Resume + Devam Et + + + Show Labels Under Icons + Simgelerin Altında Etiketleri Göster + PKGViewer diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 06c88428b..69e6c5fc7 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index c16604b85..14bd29896 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 6364ae1d6..7536b7d17 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + 开始游戏 + + + Pause + 暂停 + + + Stop + 关闭 + + + Restart + 重新启动 + + + Full Screen + 全屏 + + + Controllers + 控制器 + + + Keyboard + 键盘 + + + Refresh List + 刷新列表 + + + Resume + 继续游戏 + + + Show Labels Under Icons + 显示图标下的标签 + PKGViewer diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index fb42a43b0..f195ec1b7 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1514,6 +1514,46 @@ shadPS4 shadPS4 + + Play + Play + + + Pause + Pause + + + Stop + Stop + + + Restart + Restart + + + Full Screen + Full Screen + + + Controllers + Controllers + + + Keyboard + Keyboard + + + Refresh List + Refresh List + + + Resume + Resume + + + Show Labels Under Icons + Show Labels Under Icons + PKGViewer From 751a23af0f5a9612b8e28af1400896a3026ee331 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 28 Mar 2025 19:49:04 +0200 Subject: [PATCH 37/52] [ci skip] Qt GUI: Update Translation. (#2703) Co-authored-by: georgemoralis <4313123+georgemoralis@users.noreply.github.com> --- src/qt_gui/translations/en_US.ts | 174 ------------------------------- 1 file changed, 174 deletions(-) diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index d18609295..28d31b200 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - - - - Delete PKG File on Install - - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Only one file can be selected! - - PKG Extraction - PKG Extraction - - - Patch detected! - Patch detected! - - - PKG and Game versions match: - PKG and Game versions match: - - - Would you like to overwrite? - Would you like to overwrite? - - - PKG Version %1 is older than installed version: - PKG Version %1 is older than installed version: - - - Game is installed: - Game is installed: - - - Would you like to install Patch: - Would you like to install Patch: - - - DLC Installation - DLC Installation - - - Would you like to install DLC: %1? - Would you like to install DLC: %1? - - - DLC already installed: - DLC already installed: - - - Game already installed - Game already installed - - - PKG ERROR - PKG ERROR - - - Extracting PKG %1/%2 - Extracting PKG %1/%2 - - - Extraction Finished - Extraction Finished - - - Game successfully installed at %1 - Game successfully installed at %1 - - - File doesn't appear to be a valid PKG file - File doesn't appear to be a valid PKG file - Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found - - PKG File (*.PKG *.pkg) - - - - PKG is a patch or DLC, please install the game first! - - Game is already running! @@ -1555,73 +1448,6 @@ - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - PKG ERROR - - - Name - Name - - - Serial - Serial - - - Installed - - - - Size - Size - - - Category - - - - Type - - - - App Ver - - - - FW - - - - Region - Region - - - Flags - - - - Path - Path - - - File - File - - - Unknown - Unknown - - - Package - - - SettingsDialog From 78c8bca2bb7a1da452ba67f4813b4d504b8b4e7b Mon Sep 17 00:00:00 2001 From: Ked <58560148+k3dr1@users.noreply.github.com> Date: Sat, 29 Mar 2025 05:14:52 +0800 Subject: [PATCH 38/52] Fix support for unicode paths for game install directories (#2699) * Slightly changed how allInstallDirsDisabled is determined * Show a dialog only if no game directories are set * Changed a comment * Fixed formatting * Support for unicode paths for game install directories * Fixed game picture path conversion --- src/common/config.cpp | 2 +- src/qt_gui/game_list_frame.cpp | 3 ++- src/qt_gui/settings_dialog.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 8ead58686..d1bb89897 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -850,7 +850,7 @@ void load(const std::filesystem::path& path) { m_window_size_H = toml::find_or(gui, "mw_height", 0); const auto install_dir_array = - toml::find_or>(gui, "installDirs", {}); + toml::find_or>(gui, "installDirs", {}); try { install_dirs_enabled = toml::find>(gui, "installDirsEnabled"); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index 4c0607571..170215f3d 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -185,7 +185,8 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) { // Recompute if opacity changed or we switched to a different game if (opacity != m_last_opacity || game.pic_path != m_current_game_path) { - QImage original_image(QString::fromStdString(game.pic_path.string())); + auto image_path = game.pic_path.u8string(); + QImage original_image(QString::fromStdString({image_path.begin(), image_path.end()})); if (!original_image.isNull()) { backgroundImage = m_game_list_utils.ChangeImageOpacity( original_image, original_image.rect(), opacity / 100.0f); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index d789f6f48..383cad8fa 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -817,7 +817,7 @@ void SettingsDialog::ResetInstallFolders() { if (data.contains("GUI")) { const toml::value& gui = data.at("GUI"); const auto install_dir_array = - toml::find_or>(gui, "installDirs", {}); + toml::find_or>(gui, "installDirs", {}); std::vector install_dirs_enabled; try { From be7d646e8314ccf1f125818f3589b78d8e3262eb Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:32:06 -0700 Subject: [PATCH 39/52] externals: Remove need for cryptopp build. (#2707) --- .gitmodules | 8 - CMakeLists.txt | 4 +- externals/CMakeLists.txt | 16 +- externals/cryptopp | 1 - externals/cryptopp-cmake | 1 - src/common/aes.h | 1195 ++++++++++++++++++++++++++++++++++ src/common/sha1.h | 180 +++++ src/core/file_format/trp.cpp | 37 +- src/core/module.cpp | 11 +- 9 files changed, 1398 insertions(+), 55 deletions(-) delete mode 160000 externals/cryptopp delete mode 160000 externals/cryptopp-cmake create mode 100644 src/common/aes.h create mode 100644 src/common/sha1.h diff --git a/.gitmodules b/.gitmodules index ca229bedd..98fba2098 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,11 +1,3 @@ -[submodule "externals/cryptopp-cmake"] - path = externals/cryptopp-cmake - url = https://github.com/shadps4-emu/ext-cryptopp-cmake.git - shallow = true -[submodule "externals/cryptopp"] - path = externals/cryptopp - url = https://github.com/shadps4-emu/ext-cryptopp.git - shallow = true [submodule "externals/zlib-ng"] path = externals/zlib-ng url = https://github.com/shadps4-emu/ext-zlib-ng.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 13204f479..bd458f04e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -583,6 +583,7 @@ set(COMMON src/common/logging/backend.cpp src/common/logging/text_formatter.cpp src/common/logging/text_formatter.h src/common/logging/types.h + src/common/aes.h src/common/alignment.h src/common/arch.h src/common/assert.cpp @@ -614,6 +615,7 @@ set(COMMON src/common/logging/backend.cpp src/common/polyfill_thread.h src/common/rdtsc.cpp src/common/rdtsc.h + src/common/sha1.h src/common/signal_context.h src/common/signal_context.cpp src/common/singleton.h @@ -1022,7 +1024,7 @@ endif() create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers cryptopp::cryptopp) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 3b29a838e..d6bdda023 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -26,21 +26,7 @@ if (NOT TARGET fmt::fmt) add_subdirectory(fmt) endif() -# CryptoPP -if (NOT TARGET cryptopp::cryptopp) - set(CRYPTOPP_INSTALL OFF) - set(CRYPTOPP_BUILD_TESTING OFF) - set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp) - # cryptopp instruction set checks do not account for added compile options, - # so disable extensions in the library config to match our chosen target CPU. - set(CRYPTOPP_DISABLE_AESNI ON) - set(CRYPTOPP_DISABLE_AVX2 ON) - add_subdirectory(cryptopp-cmake) - file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h") - # remove externals/cryptopp from include directories because it contains a conflicting zlib.h file - set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp") -endif() - +# FFmpeg if (NOT TARGET FFmpeg::ffmpeg) add_subdirectory(ffmpeg-core) add_library(FFmpeg::ffmpeg ALIAS ffmpeg) diff --git a/externals/cryptopp b/externals/cryptopp deleted file mode 160000 index effed0d0b..000000000 --- a/externals/cryptopp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit effed0d0b865afc23ed67e0916f83734e4b9b3b7 diff --git a/externals/cryptopp-cmake b/externals/cryptopp-cmake deleted file mode 160000 index 2c384c282..000000000 --- a/externals/cryptopp-cmake +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2c384c28265a93358a2455e610e76393358794df diff --git a/src/common/aes.h b/src/common/aes.h new file mode 100644 index 000000000..5ca0096bd --- /dev/null +++ b/src/common/aes.h @@ -0,0 +1,1195 @@ +// SPDX-FileCopyrightText: 2015 kkAyataka +// SPDX-License-Identifier: BSL-1.0 + +#pragma once + +#include +#include +#include +#include +#include +#include + +/** AES cipher APIs */ +namespace aes { +namespace detail { + +const int kWordSize = 4; +typedef unsigned int Word; + +const int kBlockSize = 4; +/** @private */ +struct State { + Word w[4]; + Word& operator[](const int index) { + return w[index]; + } + const Word& operator[](const int index) const { + return w[index]; + } +}; + +const int kStateSize = 16; // Word * BlockSize +typedef State RoundKey; +typedef std::vector RoundKeys; + +inline void add_round_key(const RoundKey& key, State& state) { + for (int i = 0; i < kBlockSize; ++i) { + state[i] ^= key[i]; + } +} + +const unsigned char kSbox[] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16}; + +const unsigned char kInvSbox[] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d}; + +inline Word sub_word(const Word w) { + return kSbox[(w >> 0) & 0xFF] << 0 | kSbox[(w >> 8) & 0xFF] << 8 | + kSbox[(w >> 16) & 0xFF] << 16 | kSbox[(w >> 24) & 0xFF] << 24; +} + +inline Word inv_sub_word(const Word w) { + return kInvSbox[(w >> 0) & 0xFF] << 0 | kInvSbox[(w >> 8) & 0xFF] << 8 | + kInvSbox[(w >> 16) & 0xFF] << 16 | kInvSbox[(w >> 24) & 0xFF] << 24; +} + +inline void sub_bytes(State& state) { + for (int i = 0; i < kBlockSize; ++i) { + state[i] = sub_word(state[i]); + } +} + +inline void inv_sub_bytes(State& state) { + for (int i = 0; i < kBlockSize; ++i) { + state[i] = inv_sub_word(state[i]); + } +} + +inline void shift_rows(State& state) { + const State ori = {state[0], state[1], state[2], state[3]}; + for (int r = 1; r < kWordSize; ++r) { + const Word m2 = 0xFF << (r * 8); + const Word m1 = ~m2; + for (int c = 0; c < kBlockSize; ++c) { + state[c] = (state[c] & m1) | (ori[(c + r) % kBlockSize] & m2); + } + } +} + +inline void inv_shift_rows(State& state) { + const State ori = {state[0], state[1], state[2], state[3]}; + for (int r = 1; r < kWordSize; ++r) { + const Word m2 = 0xFF << (r * 8); + const Word m1 = ~m2; + for (int c = 0; c < kBlockSize; ++c) { + state[c] = (state[c] & m1) | (ori[(c + kBlockSize - r) % kWordSize] & m2); + } + } +} + +inline unsigned char mul2(const unsigned char b) { + unsigned char m2 = b << 1; + if (b & 0x80) { + m2 ^= 0x011B; + } + + return m2; +} + +inline unsigned char mul(const unsigned char b, const unsigned char m) { + unsigned char v = 0; + unsigned char t = b; + for (int i = 0; i < 8; ++i) { // 8-bits + if ((m >> i) & 0x01) { + v ^= t; + } + + t = mul2(t); + } + + return v; +} + +inline void mix_columns(State& state) { + for (int i = 0; i < kBlockSize; ++i) { + const unsigned char v0_1 = (state[i] >> 0) & 0xFF; + const unsigned char v1_1 = (state[i] >> 8) & 0xFF; + const unsigned char v2_1 = (state[i] >> 16) & 0xFF; + const unsigned char v3_1 = (state[i] >> 24) & 0xFF; + + const unsigned char v0_2 = mul2(v0_1); + const unsigned char v1_2 = mul2(v1_1); + const unsigned char v2_2 = mul2(v2_1); + const unsigned char v3_2 = mul2(v3_1); + + const unsigned char v0_3 = v0_2 ^ v0_1; + const unsigned char v1_3 = v1_2 ^ v1_1; + const unsigned char v2_3 = v2_2 ^ v2_1; + const unsigned char v3_3 = v3_2 ^ v3_1; + + state[i] = (v0_2 ^ v1_3 ^ v2_1 ^ v3_1) << 0 | (v0_1 ^ v1_2 ^ v2_3 ^ v3_1) << 8 | + (v0_1 ^ v1_1 ^ v2_2 ^ v3_3) << 16 | (v0_3 ^ v1_1 ^ v2_1 ^ v3_2) << 24; + } +} + +inline void inv_mix_columns(State& state) { + for (int i = 0; i < kBlockSize; ++i) { + const unsigned char v0 = (state[i] >> 0) & 0xFF; + const unsigned char v1 = (state[i] >> 8) & 0xFF; + const unsigned char v2 = (state[i] >> 16) & 0xFF; + const unsigned char v3 = (state[i] >> 24) & 0xFF; + + state[i] = (mul(v0, 0x0E) ^ mul(v1, 0x0B) ^ mul(v2, 0x0D) ^ mul(v3, 0x09)) << 0 | + (mul(v0, 0x09) ^ mul(v1, 0x0E) ^ mul(v2, 0x0B) ^ mul(v3, 0x0D)) << 8 | + (mul(v0, 0x0D) ^ mul(v1, 0x09) ^ mul(v2, 0x0E) ^ mul(v3, 0x0B)) << 16 | + (mul(v0, 0x0B) ^ mul(v1, 0x0D) ^ mul(v2, 0x09) ^ mul(v3, 0x0E)) << 24; + } +} + +inline Word rot_word(const Word v) { + return ((v >> 8) & 0x00FFFFFF) | ((v & 0xFF) << 24); +} + +/** + * @private + * @throws std::invalid_argument + */ +inline unsigned int get_round_count(const int key_size) { + switch (key_size) { + case 16: + return 10; + case 24: + return 12; + case 32: + return 14; + default: + throw std::invalid_argument("Invalid key size"); + } +} + +/** + * @private + * @throws std::invalid_argument + */ +inline RoundKeys expand_key(const unsigned char* key, const int key_size) { + if (key_size != 16 && key_size != 24 && key_size != 32) { + throw std::invalid_argument("Invalid key size"); + } + + const Word rcon[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36}; + + const int nb = kBlockSize; + const int nk = key_size / nb; + const int nr = get_round_count(key_size); + + std::vector w(nb * (nr + 1)); + for (int i = 0; i < nk; ++i) { + memcpy(&w[i], key + (i * kWordSize), kWordSize); + } + + for (int i = nk; i < nb * (nr + 1); ++i) { + Word t = w[i - 1]; + if (i % nk == 0) { + t = sub_word(rot_word(t)) ^ rcon[i / nk]; + } else if (nk > 6 && i % nk == 4) { + t = sub_word(t); + } + + w[i] = t ^ w[i - nk]; + } + + RoundKeys keys(nr + 1); + memcpy(&keys[0], &w[0], w.size() * kWordSize); + + return keys; +} + +inline void copy_bytes_to_state(const unsigned char data[16], State& state) { + memcpy(&state[0], data + 0, kWordSize); + memcpy(&state[1], data + 4, kWordSize); + memcpy(&state[2], data + 8, kWordSize); + memcpy(&state[3], data + 12, kWordSize); +} + +inline void copy_state_to_bytes(const State& state, unsigned char buf[16]) { + memcpy(buf + 0, &state[0], kWordSize); + memcpy(buf + 4, &state[1], kWordSize); + memcpy(buf + 8, &state[2], kWordSize); + memcpy(buf + 12, &state[3], kWordSize); +} + +inline void xor_data(unsigned char data[kStateSize], const unsigned char v[kStateSize]) { + for (int i = 0; i < kStateSize; ++i) { + data[i] ^= v[i]; + } +} + +/** increment counter (128-bit int) by 1 */ +inline void incr_counter(unsigned char counter[kStateSize]) { + unsigned n = kStateSize, c = 1; + do { + --n; + c += counter[n]; + counter[n] = c; + c >>= 8; + } while (n); +} + +inline void encrypt_state(const RoundKeys& rkeys, const unsigned char data[16], + unsigned char encrypted[16]) { + State s; + copy_bytes_to_state(data, s); + + add_round_key(rkeys[0], s); + + for (unsigned int i = 1; i < rkeys.size() - 1; ++i) { + sub_bytes(s); + shift_rows(s); + mix_columns(s); + add_round_key(rkeys[i], s); + } + + sub_bytes(s); + shift_rows(s); + add_round_key(rkeys.back(), s); + + copy_state_to_bytes(s, encrypted); +} + +inline void decrypt_state(const RoundKeys& rkeys, const unsigned char data[16], + unsigned char decrypted[16]) { + State s; + copy_bytes_to_state(data, s); + + add_round_key(rkeys.back(), s); + inv_shift_rows(s); + inv_sub_bytes(s); + + for (std::size_t i = rkeys.size() - 2; i > 0; --i) { + add_round_key(rkeys[i], s); + inv_mix_columns(s); + inv_shift_rows(s); + inv_sub_bytes(s); + } + + add_round_key(rkeys[0], s); + + copy_state_to_bytes(s, decrypted); +} + +template +std::vector key_from_string(const char (*key_str)[KeyLen]) { + std::vector key(KeyLen - 1); + memcpy(&key[0], *key_str, KeyLen - 1); + return key; +} + +inline bool is_valid_key_size(const std::size_t key_size) { + if (key_size != 16 && key_size != 24 && key_size != 32) { + return false; + } else { + return true; + } +} + +namespace gcm { + +const int kBlockBitSize = 128; +const int kBlockByteSize = kBlockBitSize / 8; + +/** + * @private + * GCM operation unit as bit. + * This library handles 128 bit little endian bit array. + * e.g. 0^127 || 1 == "000...0001" (bit string) == 1 + */ +typedef std::bitset bitset128; + +/** + * @private + * GCM operation unit. + * Little endian byte array + * + * If bitset128 is 1: 0^127 || 1 == "000...0001" (bit string) == 1 + * byte array is 0x00, 0x00, 0x00 ... 0x01 (low -> high). + * Byte array is NOT 0x01, 0x00 ... 0x00. + * + * This library handles GCM bit string in two ways. + * One is an array of bitset, which is a little endian 128-bit array's array. + * + * <- first byte + * bitset128 || bitset128 || bitset128... + * + * The other one is a byte array. + * <- first byte + * byte || byte || byte... + */ +class Block { +public: + Block() { + init_v(0, 0); + } + + Block(const unsigned char* bytes, const unsigned long bytes_size) { + init_v(bytes, bytes_size); + } + + Block(const std::vector& bytes) { + init_v(&bytes[0], bytes.size()); + } + + Block(const std::bitset<128>& bits); // implementation below + + inline unsigned char* data() { + return v_; + } + + inline const unsigned char* data() const { + return v_; + } + + inline std::bitset<128> to_bits() const { + std::bitset<128> bits; + for (int i = 0; i < 16; ++i) { + bits <<= 8; + bits |= v_[i]; + } + + return bits; + } + + inline Block operator^(const Block& b) const { + Block r; + for (int i = 0; i < 16; ++i) { + r.data()[i] = data()[i] ^ b.data()[i]; + } + return r; + } + +private: + unsigned char v_[16]; + + inline void init_v(const unsigned char* bytes, const std::size_t bytes_size) { + memset(v_, 0, sizeof(v_)); + + const std::size_t cs = (std::min)(bytes_size, static_cast(16)); + for (std::size_t i = 0; i < cs; ++i) { + v_[i] = bytes[i]; + } + } +}; + +// Workaround for clang optimization in 32-bit build via Visual Studio producing incorrect results +// (https://github.com/kkAyataka/plusaes/issues/43) +#if defined(__clang__) && defined(_WIN32) && !defined(_WIN64) +#pragma optimize("", off) +#endif +inline Block::Block(const std::bitset<128>& bits) { + init_v(0, 0); + const std::bitset<128> mask(0xFF); // 1 byte mask + for (std::size_t i = 0; i < 16; ++i) { + v_[15 - i] = static_cast(((bits >> (i * 8)) & mask).to_ulong()); + } +} +#if defined(__clang__) && defined(_WIN32) && !defined(_WIN64) +#pragma optimize("", on) +#endif + +template +unsigned long ceil(const T v) { + return static_cast(std::ceil(v) + 0.5); +} + +template +std::bitset operator||(const std::bitset& v1, const std::bitset& v2) { + std::bitset ret(v1.to_string() + v2.to_string()); + return ret; +} + +template +std::bitset lsb(const std::bitset& X) { + std::bitset r; + for (std::size_t i = 0; i < S; ++i) { + r[i] = X[i]; + } + return r; +} + +template +std::bitset msb(const std::bitset& X) { + std::bitset r; + for (std::size_t i = 0; i < S; ++i) { + r[S - 1 - i] = X[X.size() - 1 - i]; + } + return r; +} + +template +std::bitset inc32(const std::bitset X) { + const std::size_t S = 32; + + const auto a = msb(X); + const std::bitset b( + (lsb(X).to_ulong() + 1)); // % (2^32); + // lsb<32> is low 32-bit value + // Spec.'s "mod 2^S" is not necessary when S is 32 (inc32). + // ...and 2^32 is over 32-bit integer. + + return a || b; +} + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wself-assign" +#endif // __clang__ + +/** Algorithm 1 @private */ +inline Block mul_blocks(const Block X, const Block Y) { + const bitset128 R = (std::bitset<8>("11100001") || std::bitset<120>()); + + bitset128 X_bits = X.to_bits(); + bitset128 Z; + bitset128 V = Y.to_bits(); + for (int i = 127; i >= 0; --i) { + // Z + if (X_bits[i] == false) { + Z = Z; + } else { + Z = Z ^ V; + } + + // V + if (V[0] == false) { + V = V >> 1; + } else { + V = (V >> 1) ^ R; + } + } + + return Z; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ + +/** Algorithm 2 @private */ +inline Block ghash(const Block& H, const std::vector& X) { + const std::size_t m = X.size() / kBlockByteSize; + Block Ym; + for (std::size_t i = 0; i < m; ++i) { + const Block Xi(&X[i * kBlockByteSize], kBlockByteSize); + Ym = mul_blocks((Ym ^ Xi), H); + } + + return Ym; +} + +template +std::bitset make_bitset(const unsigned char* bytes, const std::size_t bytes_size) { + std::bitset bits; + for (auto i = 0u; i < bytes_size; ++i) { + bits <<= 8; + bits |= bytes[i]; + } + return bits; +} + +/** Algorithm 3 @private */ +inline std::vector gctr(const detail::RoundKeys& rkeys, const Block& ICB, + const unsigned char* X, const std::size_t X_size) { + if (!X || X_size == 0) { + return std::vector(); + } else { + const unsigned long n = ceil(X_size * 8.0 / kBlockBitSize); + std::vector Y(X_size); + + Block CB; + for (std::size_t i = 0; i < n; ++i) { + // CB + if (i == 0) { // first + CB = ICB; + } else { + CB = inc32(CB.to_bits()); + } + + // CIPH + Block eCB; + encrypt_state(rkeys, CB.data(), eCB.data()); + + // Y + int op_size = 0; + if (i < n - 1) { + op_size = kBlockByteSize; + } else { // last + op_size = (X_size % kBlockByteSize) ? (X_size % kBlockByteSize) : kBlockByteSize; + } + const Block Yi = Block(X + i * kBlockBitSize / 8, op_size) ^ eCB; + memcpy(&Y[i * kBlockByteSize], Yi.data(), op_size); + } + + return Y; + } +} + +inline void push_back(std::vector& bytes, const unsigned char* data, + const std::size_t data_size) { + bytes.insert(bytes.end(), data, data + data_size); +} + +inline void push_back(std::vector& bytes, const std::bitset<64>& bits) { + const std::bitset<64> mask(0xFF); // 1 byte mask + for (std::size_t i = 0; i < 8; ++i) { + bytes.push_back(static_cast(((bits >> ((7 - i) * 8)) & mask).to_ulong())); + } +} + +inline void push_back_zero_bits(std::vector& bytes, + const std::size_t zero_bits_size) { + const std::vector zero_bytes(zero_bits_size / 8); + bytes.insert(bytes.end(), zero_bytes.begin(), zero_bytes.end()); +} + +inline Block calc_H(const RoundKeys& rkeys) { + std::vector H_raw(gcm::kBlockByteSize); + encrypt_state(rkeys, &H_raw[0], &H_raw[0]); + return gcm::Block(H_raw); +} + +inline Block calc_J0(const Block& H, const unsigned char* iv, const std::size_t iv_size) { + if (iv_size == 12) { + const std::bitset<96> iv_bits = gcm::make_bitset<96>(iv, iv_size); + return iv_bits || std::bitset<31>() || std::bitset<1>(1); + } else { + const auto len_iv = iv_size * 8; + const auto s = 128 * gcm::ceil(len_iv / 128.0) - len_iv; + std::vector ghash_in; + gcm::push_back(ghash_in, iv, iv_size); + gcm::push_back_zero_bits(ghash_in, s + 64); + gcm::push_back(ghash_in, std::bitset<64>(len_iv)); + + return gcm::ghash(H, ghash_in); + } +} + +inline void calc_gcm_tag(const unsigned char* data, const std::size_t data_size, + const unsigned char* aadata, const std::size_t aadata_size, + const unsigned char* key, const std::size_t key_size, + const unsigned char* iv, const std::size_t iv_size, unsigned char* tag, + const std::size_t tag_size) { + const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); + const gcm::Block H = gcm::calc_H(rkeys); + const gcm::Block J0 = gcm::calc_J0(H, iv, iv_size); + + const auto lenC = data_size * 8; + const auto lenA = aadata_size * 8; + const std::size_t u = 128 * gcm::ceil(lenC / 128.0) - lenC; + const std::size_t v = 128 * gcm::ceil(lenA / 128.0) - lenA; + + std::vector ghash_in; + ghash_in.reserve((aadata_size + v / 8) + (data_size + u / 8) + 8 + 8); + gcm::push_back(ghash_in, aadata, aadata_size); + gcm::push_back_zero_bits(ghash_in, v); + gcm::push_back(ghash_in, data, data_size); + gcm::push_back_zero_bits(ghash_in, u); + gcm::push_back(ghash_in, std::bitset<64>(lenA)); + gcm::push_back(ghash_in, std::bitset<64>(lenC)); + const gcm::Block S = gcm::ghash(H, ghash_in); + const std::vector T = gcm::gctr(rkeys, J0, S.data(), gcm::kBlockByteSize); + + // return + memcpy(tag, &T[0], (std::min)(tag_size, static_cast(16))); +} + +/** Algorithm 4 and 5 @private */ +inline void crypt_gcm(const unsigned char* data, const std::size_t data_size, + const unsigned char* key, const std::size_t key_size, const unsigned char* iv, + const std::size_t iv_size, unsigned char* crypted) { + const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); + const gcm::Block H = gcm::calc_H(rkeys); + const gcm::Block J0 = gcm::calc_J0(H, iv, iv_size); + + const std::vector C = + gcm::gctr(rkeys, gcm::inc32(J0.to_bits()), data, data_size); + + if (crypted) { + memcpy(crypted, &C[0], data_size); + } +} + +} // namespace gcm + +} // namespace detail + +/** @defgroup Base Base + * Base definitions and convenient functions + * @{ */ + +/** Create 128-bit key from string. */ +inline std::vector key_from_string(const char (*key_str)[17]) { + return detail::key_from_string<17>(key_str); +} + +/** Create 192-bit key from string. */ +inline std::vector key_from_string(const char (*key_str)[25]) { + return detail::key_from_string<25>(key_str); +} + +/** Create 256-bit key from string. */ +inline std::vector key_from_string(const char (*key_str)[33]) { + return detail::key_from_string<33>(key_str); +} + +/** Calculates encrypted data size when padding is enabled. */ +inline unsigned long get_padded_encrypted_size(const unsigned long data_size) { + return data_size + detail::kStateSize - (data_size % detail::kStateSize); +} + +/** Error code */ +typedef enum { + kErrorOk = 0, + kErrorInvalidDataSize = 1, + kErrorInvalidKeySize, + kErrorInvalidBufferSize, + kErrorInvalidKey, + kErrorDeprecated, // kErrorInvalidNonceSize + kErrorInvalidIvSize, + kErrorInvalidTagSize, + kErrorInvalidTag +} Error; + +/** @} */ + +namespace detail { + +inline Error check_encrypt_cond(const unsigned long data_size, const unsigned long key_size, + const unsigned long encrypted_size, const bool pads) { + // check data size + if (!pads && (data_size % kStateSize != 0)) { + return kErrorInvalidDataSize; + } + + // check key size + if (!detail::is_valid_key_size(key_size)) { + return kErrorInvalidKeySize; + } + + // check encrypted buffer size + if (pads) { + const unsigned long required_size = get_padded_encrypted_size(data_size); + if (encrypted_size < required_size) { + return kErrorInvalidBufferSize; + } + } else { + if (encrypted_size < data_size) { + return kErrorInvalidBufferSize; + } + } + return kErrorOk; +} + +inline Error check_decrypt_cond(const unsigned long data_size, const unsigned long key_size, + const unsigned long decrypted_size, + const unsigned long* padded_size) { + // check data size + if (data_size % 16 != 0) { + return kErrorInvalidDataSize; + } + + // check key size + if (!detail::is_valid_key_size(key_size)) { + return kErrorInvalidKeySize; + } + + // check decrypted buffer size + if (!padded_size) { + if (decrypted_size < data_size) { + return kErrorInvalidBufferSize; + } + } else { + if (decrypted_size < (data_size - kStateSize)) { + return kErrorInvalidBufferSize; + } + } + + return kErrorOk; +} + +inline bool check_padding(const unsigned long padding, const unsigned char data[kStateSize]) { + if (padding > kStateSize) { + return false; + } + + for (unsigned long i = 0; i < padding; ++i) { + if (data[kStateSize - 1 - i] != padding) { + return false; + } + } + + return true; +} + +inline Error check_gcm_cond(const std::size_t key_size, const std::size_t iv_size, + const std::size_t tag_size) { + // check key size + if (!detail::is_valid_key_size(key_size)) { + return kErrorInvalidKeySize; + } + + if (iv_size < 1) { + return kErrorInvalidIvSize; + } + + // check tag size + if ((tag_size < 12 || 16 < tag_size) && (tag_size != 8) && (tag_size != 4)) { + return kErrorInvalidTagSize; + } + + return kErrorOk; +} + +} // namespace detail + +/** @defgroup ECB ECB + * ECB mode functions + * @{ */ + +/** + * Encrypts data with ECB mode. + * @param [in] data Data. + * @param [in] data_size Data size. + * If the pads is false, data size must be multiple of 16. + * @param [in] key key bytes. The key length must be 16 (128-bit), 24 (192-bit) or 32 (256-bit). + * @param [in] key_size key size. + * @param [out] encrypted Encrypted data buffer. + * @param [in] encrypted_size Encrypted data buffer size. + * @param [in] pads If this value is true, encrypted data is padded by PKCS. + * Encrypted data size must be multiple of 16. + * If the pads is true, encrypted data is padded with PKCS. + * So the data is multiple of 16, encrypted data size needs additonal 16 bytes. + * @since 1.0.0 + */ +inline Error encrypt_ecb(const unsigned char* data, const unsigned long data_size, + const unsigned char* key, const unsigned long key_size, + unsigned char* encrypted, const unsigned long encrypted_size, + const bool pads) { + const Error e = detail::check_encrypt_cond(data_size, key_size, encrypted_size, pads); + if (e != kErrorOk) { + return e; + } + + const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); + + const unsigned long bc = data_size / detail::kStateSize; + for (unsigned long i = 0; i < bc; ++i) { + detail::encrypt_state(rkeys, data + (i * detail::kStateSize), + encrypted + (i * detail::kStateSize)); + } + + if (pads) { + const int rem = data_size % detail::kStateSize; + const char pad_v = detail::kStateSize - rem; + + std::vector ib(detail::kStateSize, pad_v), ob(detail::kStateSize); + memcpy(&ib[0], data + data_size - rem, rem); + + detail::encrypt_state(rkeys, &ib[0], &ob[0]); + memcpy(encrypted + (data_size - rem), &ob[0], detail::kStateSize); + } + + return kErrorOk; +} + +/** + * Decrypts data with ECB mode. + * @param [in] data Data bytes. + * @param [in] data_size Data size. + * @param [in] key Key bytes. + * @param [in] key_size Key size. + * @param [out] decrypted Decrypted data buffer. + * @param [in] decrypted_size Decrypted data buffer size. + * @param [out] padded_size If this value is NULL, this function does not remove padding. + * If this value is not NULL, this function removes padding by PKCS + * and returns padded size using padded_size. + * @since 1.0.0 + */ +inline Error decrypt_ecb(const unsigned char* data, const unsigned long data_size, + const unsigned char* key, const unsigned long key_size, + unsigned char* decrypted, const unsigned long decrypted_size, + unsigned long* padded_size) { + const Error e = detail::check_decrypt_cond(data_size, key_size, decrypted_size, padded_size); + if (e != kErrorOk) { + return e; + } + + const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); + + const unsigned long bc = data_size / detail::kStateSize - 1; + for (unsigned long i = 0; i < bc; ++i) { + detail::decrypt_state(rkeys, data + (i * detail::kStateSize), + decrypted + (i * detail::kStateSize)); + } + + unsigned char last[detail::kStateSize] = {}; + detail::decrypt_state(rkeys, data + (bc * detail::kStateSize), last); + + if (padded_size) { + *padded_size = last[detail::kStateSize - 1]; + const unsigned long cs = detail::kStateSize - *padded_size; + + if (!detail::check_padding(*padded_size, last)) { + return kErrorInvalidKey; + } else if (decrypted_size >= (bc * detail::kStateSize) + cs) { + memcpy(decrypted + (bc * detail::kStateSize), last, cs); + } else { + return kErrorInvalidBufferSize; + } + } else { + memcpy(decrypted + (bc * detail::kStateSize), last, sizeof(last)); + } + + return kErrorOk; +} + +/** @} */ + +/** @defgroup CBC CBC + * CBC mode functions + * @{ */ + +/** + * Encrypt data with CBC mode. + * @param [in] data Data. + * @param [in] data_size Data size. + * If the pads is false, data size must be multiple of 16. + * @param [in] key key bytes. The key length must be 16 (128-bit), 24 (192-bit) or 32 (256-bit). + * @param [in] key_size key size. + * @param [in] iv Initialize vector. + * @param [out] encrypted Encrypted data buffer. + * @param [in] encrypted_size Encrypted data buffer size. + * @param [in] pads If this value is true, encrypted data is padded by PKCS. + * Encrypted data size must be multiple of 16. + * If the pads is true, encrypted data is padded with PKCS. + * So the data is multiple of 16, encrypted data size needs additonal 16 bytes. + * @since 1.0.0 + */ +inline Error encrypt_cbc(const unsigned char* data, const unsigned long data_size, + const unsigned char* key, const unsigned long key_size, + const unsigned char iv[16], unsigned char* encrypted, + const unsigned long encrypted_size, const bool pads) { + const Error e = detail::check_encrypt_cond(data_size, key_size, encrypted_size, pads); + if (e != kErrorOk) { + return e; + } + + const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); + + unsigned char s[detail::kStateSize] = {}; // encrypting data + + // calculate padding value + const bool ge16 = (data_size >= detail::kStateSize); + const int rem = data_size % detail::kStateSize; + const unsigned char pad_v = detail::kStateSize - rem; + + // encrypt 1st state + if (ge16) { + memcpy(s, data, detail::kStateSize); + } else { + memset(s, pad_v, detail::kStateSize); + memcpy(s, data, data_size); + } + if (iv) { + detail::xor_data(s, iv); + } + detail::encrypt_state(rkeys, s, encrypted); + + // encrypt mid + const unsigned long bc = data_size / detail::kStateSize; + for (unsigned long i = 1; i < bc; ++i) { + const long offset = i * detail::kStateSize; + memcpy(s, data + offset, detail::kStateSize); + detail::xor_data(s, encrypted + offset - detail::kStateSize); + + detail::encrypt_state(rkeys, s, encrypted + offset); + } + + // enctypt last + if (pads && ge16) { + std::vector ib(detail::kStateSize, pad_v), ob(detail::kStateSize); + memcpy(&ib[0], data + data_size - rem, rem); + + detail::xor_data(&ib[0], encrypted + (bc - 1) * detail::kStateSize); + + detail::encrypt_state(rkeys, &ib[0], &ob[0]); + memcpy(encrypted + (data_size - rem), &ob[0], detail::kStateSize); + } + + return kErrorOk; +} + +/** + * Decrypt data with CBC mode. + * @param [in] data Data bytes. + * @param [in] data_size Data size. + * @param [in] key Key bytes. + * @param [in] key_size Key size. + * @param [in] iv Initialize vector. + * @param [out] decrypted Decrypted data buffer. + * @param [in] decrypted_size Decrypted data buffer size. + * @param [out] padded_size If this value is NULL, this function does not remove padding. + * If this value is not NULL, this function removes padding by PKCS + * and returns padded size using padded_size. + * @since 1.0.0 + */ +inline Error decrypt_cbc(const unsigned char* data, const unsigned long data_size, + const unsigned char* key, const unsigned long key_size, + const unsigned char iv[16], unsigned char* decrypted, + const unsigned long decrypted_size, unsigned long* padded_size) { + const Error e = detail::check_decrypt_cond(data_size, key_size, decrypted_size, padded_size); + if (e != kErrorOk) { + return e; + } + + const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); + + // decrypt 1st state + detail::decrypt_state(rkeys, data, decrypted); + if (iv) { + detail::xor_data(decrypted, iv); + } + + // decrypt mid + const unsigned long bc = data_size / detail::kStateSize - 1; + for (unsigned long i = 1; i < bc; ++i) { + const long offset = i * detail::kStateSize; + detail::decrypt_state(rkeys, data + offset, decrypted + offset); + detail::xor_data(decrypted + offset, data + offset - detail::kStateSize); + } + + // decrypt last + unsigned char last[detail::kStateSize] = {}; + if (data_size > detail::kStateSize) { + detail::decrypt_state(rkeys, data + (bc * detail::kStateSize), last); + detail::xor_data(last, data + (bc * detail::kStateSize - detail::kStateSize)); + } else { + memcpy(last, decrypted, data_size); + memset(decrypted, 0, decrypted_size); + } + + if (padded_size) { + *padded_size = last[detail::kStateSize - 1]; + const unsigned long cs = detail::kStateSize - *padded_size; + + if (!detail::check_padding(*padded_size, last)) { + return kErrorInvalidKey; + } else if (decrypted_size >= (bc * detail::kStateSize) + cs) { + memcpy(decrypted + (bc * detail::kStateSize), last, cs); + } else { + return kErrorInvalidBufferSize; + } + } else { + memcpy(decrypted + (bc * detail::kStateSize), last, sizeof(last)); + } + + return kErrorOk; +} + +/** @} */ + +/** @defgroup GCM GCM + * GCM mode functions + * @{ */ + +/** + * Encrypts data with GCM mode and gets an authentication tag. + * + * You can specify iv size and tag size. + * But usually you should use the other overloaded function whose iv and tag size is fixed. + * + * @returns kErrorOk + * @returns kErrorInvalidKeySize + * @returns kErrorInvalidIvSize + * @returns kErrorInvalidTagSize + */ +inline Error encrypt_gcm(unsigned char* data, const std::size_t data_size, + const unsigned char* aadata, const std::size_t aadata_size, + const unsigned char* key, const std::size_t key_size, + const unsigned char* iv, const std::size_t iv_size, unsigned char* tag, + const std::size_t tag_size) { + const Error err = detail::check_gcm_cond(key_size, iv_size, tag_size); + if (err != kErrorOk) { + return err; + } + + detail::gcm::crypt_gcm(data, data_size, key, key_size, iv, iv_size, data); + detail::gcm::calc_gcm_tag(data, data_size, aadata, aadata_size, key, key_size, iv, iv_size, tag, + tag_size); + + return kErrorOk; +} + +/** + * Encrypts data with GCM mode and gets an authentication tag. + * + * @param [in,out] data Input data and output buffer. + * This buffer is replaced with encrypted data. + * @param [in] data_size data size + * @param [in] aadata Additional Authenticated data + * @param [in] aadata_size aadata size + * @param [in] key Cipher key + * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32 + * (256-bit). + * @param [in] iv Initialization vector + * @param [out] tag Calculated authentication tag data + * + * @returns kErrorOk + * @returns kErrorInvalidKeySize + */ +inline Error encrypt_gcm(unsigned char* data, const std::size_t data_size, + const unsigned char* aadata, const std::size_t aadata_size, + const unsigned char* key, const std::size_t key_size, + const unsigned char (*iv)[12], unsigned char (*tag)[16]) { + return encrypt_gcm(data, data_size, aadata, aadata_size, key, key_size, *iv, 12, *tag, 16); +} + +/** + * Decrypts data with GCM mode and checks an authentication tag. + * + * You can specify iv size and tag size. + * But usually you should use the other overloaded function whose iv and tag size is fixed. + * + * @returns kErrorOk + * @returns kErrorInvalidKeySize + * @returns kErrorInvalidIvSize + * @returns kErrorInvalidTagSize + * @returns kErrorInvalidTag + */ +inline Error decrypt_gcm(unsigned char* data, const std::size_t data_size, + const unsigned char* aadata, const std::size_t aadata_size, + const unsigned char* key, const std::size_t key_size, + const unsigned char* iv, const std::size_t iv_size, + const unsigned char* tag, const std::size_t tag_size) { + const Error err = detail::check_gcm_cond(key_size, iv_size, tag_size); + if (err != kErrorOk) { + return err; + } + + unsigned char* C = data; + const auto C_size = data_size; + unsigned char tagd[16] = {}; + detail::gcm::calc_gcm_tag(C, C_size, aadata, aadata_size, key, key_size, iv, iv_size, tagd, 16); + + if (memcmp(tag, tagd, tag_size) != 0) { + return kErrorInvalidTag; + } else { + detail::gcm::crypt_gcm(C, C_size, key, key_size, iv, iv_size, C); + + return kErrorOk; + } +} + +/** + * Decrypts data with GCM mode and checks an authentication tag. + * + * @param [in,out] data Input data and output buffer. + * This buffer is replaced with decrypted data. + * @param [in] data_size data size + * @param [in] aadata Additional Authenticated data + * @param [in] aadata_size aadata size + * @param [in] key Cipher key + * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32 + * (256-bit). + * @param [in] iv Initialization vector + * @param [in] tag Authentication tag data + * + * @returns kErrorOk + * @returns kErrorInvalidKeySize + * @returns kErrorInvalidTag + */ +inline Error decrypt_gcm(unsigned char* data, const std::size_t data_size, + const unsigned char* aadata, const std::size_t aadata_size, + const unsigned char* key, const std::size_t key_size, + const unsigned char (*iv)[12], const unsigned char (*tag)[16]) { + return decrypt_gcm(data, data_size, aadata, aadata_size, key, key_size, *iv, 12, *tag, 16); +} + +/** @} */ + +/** @defgroup CTR CTR + * CTR mode function + * @{ */ + +/** + * Encrypts or decrypt data in-place with CTR mode. + * + * @param [in,out] data Input data and output buffer. + * This buffer is replaced with encrypted / decrypted data. + * @param [in] data_size Data size. + * @param [in] key Cipher key + * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32 + * (256-bit). + * @param [in] nonce Nonce of the counter initialization. + * + * @returns kErrorOk + * @returns kErrorInvalidKeySize + * @since 1.0.0 + */ +inline Error crypt_ctr(unsigned char* data, const std::size_t data_size, const unsigned char* key, + const std::size_t key_size, const unsigned char (*nonce)[16]) { + if (!detail::is_valid_key_size(key_size)) + return kErrorInvalidKeySize; + const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); + + unsigned long pos = 0; + unsigned long blkpos = detail::kStateSize; + unsigned char blk[detail::kStateSize] = {}; + unsigned char counter[detail::kStateSize] = {}; + memcpy(counter, nonce, 16); + + while (pos < data_size) { + if (blkpos == detail::kStateSize) { + detail::encrypt_state(rkeys, counter, blk); + detail::incr_counter(counter); + blkpos = 0; + } + data[pos++] ^= blk[blkpos++]; + } + + return kErrorOk; +} + +/** @} */ + +} // namespace aes diff --git a/src/common/sha1.h b/src/common/sha1.h new file mode 100644 index 000000000..fad849dcc --- /dev/null +++ b/src/common/sha1.h @@ -0,0 +1,180 @@ +// SPDX-FileCopyrightText: 2012 SAURAV MOHAPATRA +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +namespace sha1 { +class SHA1 { +public: + typedef uint32_t digest32_t[5]; + typedef uint8_t digest8_t[20]; + inline static uint32_t LeftRotate(uint32_t value, size_t count) { + return (value << count) ^ (value >> (32 - count)); + } + SHA1() { + reset(); + } + virtual ~SHA1() {} + SHA1(const SHA1& s) { + *this = s; + } + const SHA1& operator=(const SHA1& s) { + memcpy(m_digest, s.m_digest, 5 * sizeof(uint32_t)); + memcpy(m_block, s.m_block, 64); + m_blockByteIndex = s.m_blockByteIndex; + m_byteCount = s.m_byteCount; + return *this; + } + SHA1& reset() { + m_digest[0] = 0x67452301; + m_digest[1] = 0xEFCDAB89; + m_digest[2] = 0x98BADCFE; + m_digest[3] = 0x10325476; + m_digest[4] = 0xC3D2E1F0; + m_blockByteIndex = 0; + m_byteCount = 0; + return *this; + } + SHA1& processByte(uint8_t octet) { + this->m_block[this->m_blockByteIndex++] = octet; + ++this->m_byteCount; + if (m_blockByteIndex == 64) { + this->m_blockByteIndex = 0; + processBlock(); + } + return *this; + } + SHA1& processBlock(const void* const start, const void* const end) { + const uint8_t* begin = static_cast(start); + const uint8_t* finish = static_cast(end); + while (begin != finish) { + processByte(*begin); + begin++; + } + return *this; + } + SHA1& processBytes(const void* const data, size_t len) { + const uint8_t* block = static_cast(data); + processBlock(block, block + len); + return *this; + } + const uint32_t* getDigest(digest32_t digest) { + size_t bitCount = this->m_byteCount * 8; + processByte(0x80); + if (this->m_blockByteIndex > 56) { + while (m_blockByteIndex != 0) { + processByte(0); + } + while (m_blockByteIndex < 56) { + processByte(0); + } + } else { + while (m_blockByteIndex < 56) { + processByte(0); + } + } + processByte(0); + processByte(0); + processByte(0); + processByte(0); + processByte(static_cast((bitCount >> 24) & 0xFF)); + processByte(static_cast((bitCount >> 16) & 0xFF)); + processByte(static_cast((bitCount >> 8) & 0xFF)); + processByte(static_cast((bitCount) & 0xFF)); + + memcpy(digest, m_digest, 5 * sizeof(uint32_t)); + return digest; + } + const uint8_t* getDigestBytes(digest8_t digest) { + digest32_t d32; + getDigest(d32); + size_t di = 0; + digest[di++] = ((d32[0] >> 24) & 0xFF); + digest[di++] = ((d32[0] >> 16) & 0xFF); + digest[di++] = ((d32[0] >> 8) & 0xFF); + digest[di++] = ((d32[0]) & 0xFF); + + digest[di++] = ((d32[1] >> 24) & 0xFF); + digest[di++] = ((d32[1] >> 16) & 0xFF); + digest[di++] = ((d32[1] >> 8) & 0xFF); + digest[di++] = ((d32[1]) & 0xFF); + + digest[di++] = ((d32[2] >> 24) & 0xFF); + digest[di++] = ((d32[2] >> 16) & 0xFF); + digest[di++] = ((d32[2] >> 8) & 0xFF); + digest[di++] = ((d32[2]) & 0xFF); + + digest[di++] = ((d32[3] >> 24) & 0xFF); + digest[di++] = ((d32[3] >> 16) & 0xFF); + digest[di++] = ((d32[3] >> 8) & 0xFF); + digest[di++] = ((d32[3]) & 0xFF); + + digest[di++] = ((d32[4] >> 24) & 0xFF); + digest[di++] = ((d32[4] >> 16) & 0xFF); + digest[di++] = ((d32[4] >> 8) & 0xFF); + digest[di++] = ((d32[4]) & 0xFF); + return digest; + } + +protected: + void processBlock() { + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) { + w[i] = (m_block[i * 4 + 0] << 24); + w[i] |= (m_block[i * 4 + 1] << 16); + w[i] |= (m_block[i * 4 + 2] << 8); + w[i] |= (m_block[i * 4 + 3]); + } + for (size_t i = 16; i < 80; i++) { + w[i] = LeftRotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + } + + uint32_t a = m_digest[0]; + uint32_t b = m_digest[1]; + uint32_t c = m_digest[2]; + uint32_t d = m_digest[3]; + uint32_t e = m_digest[4]; + + for (std::size_t i = 0; i < 80; ++i) { + uint32_t f = 0; + uint32_t k = 0; + + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = LeftRotate(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = LeftRotate(b, 30); + b = a; + a = temp; + } + + m_digest[0] += a; + m_digest[1] += b; + m_digest[2] += c; + m_digest[3] += d; + m_digest[4] += e; + } + +private: + digest32_t m_digest; + uint8_t m_block[64]; + size_t m_blockByteIndex; + size_t m_byteCount; +}; +} // namespace sha1 diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp index 311bd0b9d..a5d11b0eb 100644 --- a/src/core/file_format/trp.cpp +++ b/src/core/file_format/trp.cpp @@ -1,35 +1,24 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include - +#include "common/aes.h" #include "common/config.h" #include "common/logging/log.h" #include "common/path_util.h" #include "core/file_format/trp.h" -static void DecryptEFSM(std::span trophyKey, - std::span NPcommID, - std::span efsmIv, std::span ciphertext, - std::span decrypted) { +static void DecryptEFSM(std::span trophyKey, std::span NPcommID, + std::span efsmIv, std::span ciphertext, + std::span decrypted) { + // Step 1: Encrypt NPcommID + std::array trophyIv{}; + std::array trpKey; + aes::encrypt_cbc(NPcommID.data(), NPcommID.size(), trophyKey.data(), trophyKey.size(), + trophyIv.data(), trpKey.data(), trpKey.size(), false); - // step 1: Encrypt NPcommID - CryptoPP::CBC_Mode::Encryption encrypt; - - std::vector trophyIv(16, 0); - std::vector trpKey(16); - - encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data()); - encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16); - - // step 2: decrypt efsm. - CryptoPP::CBC_Mode::Decryption decrypt; - decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data()); - - for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) { - decrypt.ProcessData(decrypted.data() + i, ciphertext.data() + i, CryptoPP::AES::BLOCKSIZE); - } + // Step 2: Decrypt EFSM + aes::decrypt_cbc(ciphertext.data(), ciphertext.size(), trpKey.data(), trpKey.size(), + efsmIv.data(), decrypted.data(), decrypted.size(), nullptr); } TRP::TRP() = default; @@ -80,7 +69,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit return false; } - std::array user_key{}; + std::array user_key{}; hexToBytes(user_key_str.c_str(), user_key.data()); for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) { diff --git a/src/core/module.cpp b/src/core/module.cpp index a18c1141a..1004f4404 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include - #include "common/alignment.h" #include "common/arch.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/memory_patcher.h" +#include "common/sha1.h" #include "common/string_util.h" #include "core/aerolib/aerolib.h" #include "core/cpu_patches.h" @@ -65,11 +64,13 @@ static std::string StringToNid(std::string_view symbol) { std::memcpy(input.data(), symbol.data(), symbol.size()); std::memcpy(input.data() + symbol.size(), Salt.data(), Salt.size()); - std::array hash; - CryptoPP::SHA1().CalculateDigest(hash.data(), input.data(), input.size()); + sha1::SHA1::digest8_t hash; + sha1::SHA1 sha; + sha.processBytes(input.data(), input.size()); + sha.getDigestBytes(hash); u64 digest; - std::memcpy(&digest, hash.data(), sizeof(digest)); + std::memcpy(&digest, hash, sizeof(digest)); static constexpr std::string_view codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; From df9151481c56512d05fc939313e3c944eac87819 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Sat, 29 Mar 2025 08:32:57 +0000 Subject: [PATCH 40/52] Fix the "Open Update Folder" for folders ending with "-patch" (#2712) * fix open update folder * Update gui_context_menus.h --- src/qt_gui/gui_context_menus.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index 7dcb006ba..c13388bbc 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -141,14 +141,16 @@ public: Common::FS::PathToQString(open_update_path, m_games[itemID].path); open_update_path += "-UPDATE"; if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { + QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); + } else { Common::FS::PathToQString(open_update_path, m_games[itemID].path); open_update_path += "-patch"; if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { + QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); + } else { QMessageBox::critical(nullptr, tr("Error"), QString(tr("This game has no update folder to open!"))); } - } else { - QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); } } From 9dbc79dc96a4cf439adbead5563e46d1eb301391 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 29 Mar 2025 11:03:40 +0200 Subject: [PATCH 41/52] New Crowdin updates (#2705) * New translations en_us.ts (Albanian) * New translations en_us.ts (Swedish) * New translations en_us.ts (Polish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Spanish) * New translations en_us.ts (Romanian) * New translations en_us.ts (French) * New translations en_us.ts (Arabic) * New translations en_us.ts (Danish) * New translations en_us.ts (German) * New translations en_us.ts (Greek) * New translations en_us.ts (Finnish) * New translations en_us.ts (Hungarian) * New translations en_us.ts (Italian) * New translations en_us.ts (Japanese) * New translations en_us.ts (Korean) * New translations en_us.ts (Lithuanian) * New translations en_us.ts (Dutch) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Russian) * New translations en_us.ts (Turkish) * New translations en_us.ts (Ukrainian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Chinese Traditional) * New translations en_us.ts (Vietnamese) * New translations en_us.ts (Indonesian) * New translations en_us.ts (Persian) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Swedish) * New translations en_us.ts (French) * New translations en_us.ts (Indonesian) * New translations en_us.ts (Spanish) * New translations en_us.ts (Norwegian Bokmal) --- src/qt_gui/translations/ar_SA.ts | 174 --------------------------- src/qt_gui/translations/da_DK.ts | 174 --------------------------- src/qt_gui/translations/de_DE.ts | 174 --------------------------- src/qt_gui/translations/el_GR.ts | 174 --------------------------- src/qt_gui/translations/es_ES.ts | 194 ++---------------------------- src/qt_gui/translations/fa_IR.ts | 174 --------------------------- src/qt_gui/translations/fi_FI.ts | 174 --------------------------- src/qt_gui/translations/fr_FR.ts | 200 ++----------------------------- src/qt_gui/translations/hu_HU.ts | 174 --------------------------- src/qt_gui/translations/id_ID.ts | 200 ++----------------------------- src/qt_gui/translations/it_IT.ts | 174 --------------------------- src/qt_gui/translations/ja_JP.ts | 174 --------------------------- src/qt_gui/translations/ko_KR.ts | 174 --------------------------- src/qt_gui/translations/lt_LT.ts | 174 --------------------------- src/qt_gui/translations/nb_NO.ts | 194 ++---------------------------- src/qt_gui/translations/nl_NL.ts | 174 --------------------------- src/qt_gui/translations/pl_PL.ts | 174 --------------------------- src/qt_gui/translations/pt_BR.ts | 174 --------------------------- src/qt_gui/translations/pt_PT.ts | 174 --------------------------- src/qt_gui/translations/ro_RO.ts | 174 --------------------------- src/qt_gui/translations/ru_RU.ts | 174 --------------------------- src/qt_gui/translations/sq_AL.ts | 174 --------------------------- src/qt_gui/translations/sv_SE.ts | 194 ++---------------------------- src/qt_gui/translations/tr_TR.ts | 174 --------------------------- src/qt_gui/translations/uk_UA.ts | 174 --------------------------- src/qt_gui/translations/vi_VN.ts | 174 --------------------------- src/qt_gui/translations/zh_CN.ts | 174 --------------------------- src/qt_gui/translations/zh_TW.ts | 174 --------------------------- 28 files changed, 56 insertions(+), 4928 deletions(-) diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index 9808fdbe6..f5503e189 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -882,10 +882,6 @@ Error creating shortcut! خطأ في إنشاء الاختصار - - Install PKG - PKG تثبيت - Game اللعبة @@ -978,25 +974,6 @@ أزرار التحكم - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - اختر المجلد - - - Select which directory you want to install to. - حدد الدليل الذي تريد تثبيت إليه. - - - Install All Queued to Selected Folder - تثبيت كل قائمة الانتظار إلى المجلد المحدد - - - Delete PKG File on Install - حذف مِلَفّ PKG عند التثبيت - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Elf فتح/إضافة مجلد - - Install Packages (PKG) - (PKG) تثبيت الحزم - Boot Game تشغيل اللعبة @@ -1234,10 +1207,6 @@ Configure... ...تكوين - - Install application from a .pkg file - .pkg تثبيت التطبيق من ملف - Recent Games الألعاب الأخيرة @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. لم يتم العثور على ألعاب. الرجاء إضافة ألعابك إلى مكتبتك أولاً. - - PKG Viewer - عارض PKG - Search... ...بحث @@ -1426,70 +1391,6 @@ Only one file can be selected! !يمكن تحديد ملف واحد فقط - - PKG Extraction - PKG استخراج - - - Patch detected! - تم اكتشاف تصحيح! - - - PKG and Game versions match: - :واللعبة تتطابق إصدارات PKG - - - Would you like to overwrite? - هل ترغب في الكتابة فوق الملف الموجود؟ - - - PKG Version %1 is older than installed version: - :أقدم من الإصدار المثبت PKG Version %1 - - - Game is installed: - :اللعبة مثبتة - - - Would you like to install Patch: - :هل ترغب في تثبيت التصحيح - - - DLC Installation - تثبيت المحتوى القابل للتنزيل - - - Would you like to install DLC: %1? - هل ترغب في تثبيت المحتوى القابل للتنزيل: 1%؟ - - - DLC already installed: - :المحتوى القابل للتنزيل مثبت بالفعل - - - Game already installed - اللعبة مثبتة بالفعل - - - PKG ERROR - PKG خطأ في - - - Extracting PKG %1/%2 - PKG %1/%2 جاري استخراج - - - Extraction Finished - اكتمل الاستخراج - - - Game successfully installed at %1 - تم تثبيت اللعبة بنجاح في %1 - - - File doesn't appear to be a valid PKG file - يبدو أن الملف ليس ملف PKG صالحًا - Run Game تشغيل اللعبة @@ -1498,14 +1399,6 @@ Eboot.bin file not found لم يتم العثور على ملف Eboot.bin - - PKG File (*.PKG *.pkg) - ملف PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG هو تصحيح أو DLC، يرجى تثبيت اللعبة أولاً! - Game is already running! اللعبة قيد التشغيل بالفعل! @@ -1555,73 +1448,6 @@ إظهار العلامات أسفل الأيقونات - - PKGViewer - - Open Folder - فتح المجلد - - - PKG ERROR - PKG خطأ في - - - Name - اسم - - - Serial - سيريال - - - Installed - مثبت - - - Size - حجم - - - Category - الفئة - - - Type - النوع - - - App Ver - إصدار - - - FW - FW - - - Region - منطقة - - - Flags - Flags - - - Path - مسار - - - File - ملف - - - Unknown - غير معروف - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 1547a0e13..658ac118f 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Kun én fil kan vælges! - - PKG Extraction - PKG-udtrækning - - - Patch detected! - Opdatering detekteret! - - - PKG and Game versions match: - PKG og spilversioner matcher: - - - Would you like to overwrite? - Vil du overskrive? - - - PKG Version %1 is older than installed version: - PKG Version %1 er ældre end den installerede version: - - - Game is installed: - Spillet er installeret: - - - Would you like to install Patch: - Vil du installere opdateringen: - - - DLC Installation - DLC Installation - - - Would you like to install DLC: %1? - Vil du installere DLC: %1? - - - DLC already installed: - DLC allerede installeret: - - - Game already installed - Spillet er allerede installeret - - - PKG ERROR - PKG FEJL - - - Extracting PKG %1/%2 - Udvinding af PKG %1/%2 - - - Extraction Finished - Udvinding afsluttet - - - Game successfully installed at %1 - Spillet blev installeret succesfuldt på %1 - - - File doesn't appear to be a valid PKG file - Filen ser ikke ud til at være en gyldig PKG-fil - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - PKG FEJL - - - Name - Navn - - - Serial - Seriel - - - Installed - Installed - - - Size - Størrelse - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Region - - - Flags - Flags - - - Path - Sti - - - File - File - - - Unknown - Ukendt - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index c0e43065b..b6cd3105f 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -882,10 +882,6 @@ Error creating shortcut! Fehler beim Erstellen der Verknüpfung! - - Install PKG - PKG installieren - Game Spiel @@ -978,25 +974,6 @@ Tastenbelegung - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Wähle Ordner - - - Select which directory you want to install to. - Wählen Sie das Verzeichnis aus, in das Sie installieren möchten. - - - Install All Queued to Selected Folder - Installieren Sie alles aus der Warteschlange in den ausgewählten Ordner - - - Delete PKG File on Install - PKG-Datei beim Installieren löschen - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Elf-Ordner öffnen/hinzufügen - - Install Packages (PKG) - Pakete installieren (PKG) - Boot Game Spiel starten @@ -1234,10 +1207,6 @@ Configure... Konfigurieren... - - Install application from a .pkg file - Installiere Anwendung aus .pkg-Datei - Recent Games Zuletzt gespielt @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG-Anschauer - Search... Suchen... @@ -1426,70 +1391,6 @@ Only one file can be selected! Es kann nur eine Datei ausgewählt werden! - - PKG Extraction - PKG-Extraktion - - - Patch detected! - Patch erkannt! - - - PKG and Game versions match: - PKG- und Spielversionen stimmen überein: - - - Would you like to overwrite? - Willst du überschreiben? - - - PKG Version %1 is older than installed version: - PKG-Version %1 ist älter als die installierte Version: - - - Game is installed: - Spiel ist installiert: - - - Would you like to install Patch: - Willst du den Patch installieren: - - - DLC Installation - DLC-Installation - - - Would you like to install DLC: %1? - Willst du das DLC installieren: %1? - - - DLC already installed: - DLC bereits installiert: - - - Game already installed - Spiel bereits installiert - - - PKG ERROR - PKG-FEHLER - - - Extracting PKG %1/%2 - Extrahiere PKG %1/%2 - - - Extraction Finished - Extraktion abgeschlossen - - - Game successfully installed at %1 - Spiel erfolgreich installiert auf %1 - - - File doesn't appear to be a valid PKG file - Die Datei scheint keine gültige PKG-Datei zu sein - Run Game Spiel ausführen @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin Datei nicht gefunden - - PKG File (*.PKG *.pkg) - PKG-Datei (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG ist ein Patch oder DLC, bitte installieren Sie zuerst das Spiel! - Game is already running! Spiel läuft bereits! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Ordner öffnen - - - PKG ERROR - PKG-FEHLER - - - Name - Name - - - Serial - Seriennummer - - - Installed - Installiert - - - Size - Größe - - - Category - Kategorie - - - Type - Typ - - - App Ver - App Ver - - - FW - FW - - - Region - Region - - - Flags - Markierungen - - - Path - Pfad - - - File - Datei - - - Unknown - Unbekannt - - - Package - Paket - - SettingsDialog diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index e6fa989aa..d1cf0d4a6 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Μπορεί να επιλεγεί μόνο ένα αρχείο! - - PKG Extraction - Εξαγωγή PKG - - - Patch detected! - Αναγνώριση ενημέρωσης! - - - PKG and Game versions match: - Οι εκδόσεις PKG και παιχνιδιού ταιριάζουν: - - - Would you like to overwrite? - Θέλετε να αντικαταστήσετε; - - - PKG Version %1 is older than installed version: - Η έκδοση PKG %1 είναι παλαιότερη από την εγκατεστημένη έκδοση: - - - Game is installed: - Το παιχνίδι είναι εγκατεστημένο: - - - Would you like to install Patch: - Θέλετε να εγκαταστήσετε την ενημέρωση: - - - DLC Installation - Εγκατάσταση DLC - - - Would you like to install DLC: %1? - Θέλετε να εγκαταστήσετε το DLC: %1; - - - DLC already installed: - DLC ήδη εγκατεστημένο: - - - Game already installed - Παιχνίδι ήδη εγκατεστημένο - - - PKG ERROR - ΣΦΑΛΜΑ PKG - - - Extracting PKG %1/%2 - Εξαγωγή PKG %1/%2 - - - Extraction Finished - Η εξαγωγή ολοκληρώθηκε - - - Game successfully installed at %1 - Το παιχνίδι εγκαταστάθηκε επιτυχώς στο %1 - - - File doesn't appear to be a valid PKG file - Η αρχείο δεν φαίνεται να είναι έγκυρο αρχείο PKG - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - ΣΦΑΛΜΑ PKG - - - Name - Όνομα - - - Serial - Σειριακός αριθμός - - - Installed - Installed - - - Size - Μέγεθος - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Περιοχή - - - Flags - Flags - - - Path - Διαδρομή - - - File - File - - - Unknown - Άγνωστο - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 288c445c3..bbd49f61d 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -882,10 +882,6 @@ Error creating shortcut! ¡Error al crear el acceso directo! - - Install PKG - Instalar PKG - Game Juego @@ -978,25 +974,6 @@ Asignación de Teclas - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Elegir carpeta - - - Select which directory you want to install to. - Selecciona el directorio de instalación. - - - Install All Queued to Selected Folder - Instalar toda la cola en la carpeta seleccionada - - - Delete PKG File on Install - Eliminar archivo PKG tras la instalación - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Abrir/Agregar carpeta Elf - - Install Packages (PKG) - Instalar paquetes (PKG) - Boot Game Iniciar juego @@ -1234,10 +1207,6 @@ Configure... Configurar... - - Install application from a .pkg file - Instalar aplicación desde un archivo .pkg - Recent Games Juegos recientes @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No se encontraron juegos. Por favor, añade tus juegos a tu biblioteca primero. - - PKG Viewer - Vista PKG - Search... Buscar... @@ -1426,70 +1391,6 @@ Only one file can be selected! ¡Solo se puede seleccionar un archivo! - - PKG Extraction - Extracción de PKG - - - Patch detected! - ¡Actualización detectada! - - - PKG and Game versions match: - Las versiones de PKG y del juego coinciden: - - - Would you like to overwrite? - ¿Desea sobrescribir? - - - PKG Version %1 is older than installed version: - La versión de PKG %1 es más antigua que la versión instalada: - - - Game is installed: - El juego está instalado: - - - Would you like to install Patch: - ¿Desea instalar la actualización: - - - DLC Installation - Instalación de DLC - - - Would you like to install DLC: %1? - ¿Desea instalar el DLC: %1? - - - DLC already installed: - DLC ya instalado: - - - Game already installed - Juego ya instalado - - - PKG ERROR - ERROR PKG - - - Extracting PKG %1/%2 - Extrayendo PKG %1/%2 - - - Extraction Finished - Extracción terminada - - - Game successfully installed at %1 - Juego instalado exitosamente en %1 - - - File doesn't appear to be a valid PKG file - El archivo parece no ser un archivo PKG válido - Run Game Ejecutar juego @@ -1498,14 +1399,6 @@ Eboot.bin file not found Archivo Eboot.bin no encontrado - - PKG File (*.PKG *.pkg) - Archivo PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - El archivo PKG es un parche o DLC, ¡debes instalar el juego primero! - Game is already running! ¡El juego ya se está ejecutando! @@ -1516,110 +1409,43 @@ Play - Play + Jugar Pause - Pause + Pausar Stop - Stop + Detener Restart - Restart + Reiniciar Full Screen - Full Screen + Pantalla completa Controllers - Controllers + Controles Keyboard - Keyboard + Teclado Refresh List - Refresh List + Actualizar lista Resume - Resume + Reanudar Show Labels Under Icons - Show Labels Under Icons - - - - PKGViewer - - Open Folder - Abrir Carpeta - - - PKG ERROR - ERROR PKG - - - Name - Nombre - - - Serial - Número de Serie - - - Installed - Instalado - - - Size - Tamaño - - - Category - Categoría - - - Type - Tipo - - - App Ver - Versión de la Aplicación - - - FW - FW - - - Region - Región - - - Flags - Etiquetas - - - Path - Ruta - - - File - Archivo - - - Unknown - Desconocido - - - Package - Paquete + Mostrar etiquetas debajo de los iconos diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index 1b8813a80..f1f2c62ab 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -882,10 +882,6 @@ Error creating shortcut! مشکلی در هنگام ساخت میانبر بوجود آمد! - - Install PKG - نصب PKG - Game بازی @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - ShadPS4 - انتخاب محل نصب بازی - - - Select which directory you want to install to. - محلی را که می‌خواهید در آن نصب شود، انتخاب کنید. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder ELF بازکردن/ساختن پوشه - - Install Packages (PKG) - نصب بسته (PKG) - Boot Game اجرای بازی @@ -1234,10 +1207,6 @@ Configure... ...تنظیمات - - Install application from a .pkg file - .PKG نصب بازی از فایل - Recent Games بازی های اخیر @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG مشاهده گر - Search... جست و جو... @@ -1426,70 +1391,6 @@ Only one file can be selected! فقط یک فایل انتخاب کنید! - - PKG Extraction - PKG استخراج فایل - - - Patch detected! - پچ شناسایی شد! - - - PKG and Game versions match: - و نسخه بازی همخوانی دارد PKG فایل: - - - Would you like to overwrite? - آیا مایل به جایگزینی فایل هستید؟ - - - PKG Version %1 is older than installed version: - نسخه فایل PKG %1 قدیمی تر از نسخه نصب شده است: - - - Game is installed: - بازی نصب شد: - - - Would you like to install Patch: - آیا مایل به نصب پچ هستید: - - - DLC Installation - نصب DLC - - - Would you like to install DLC: %1? - آیا مایل به نصب DLC هستید: %1 - - - DLC already installed: - قبلا نصب شده DLC این: - - - Game already installed - این بازی قبلا نصب شده - - - PKG ERROR - PKG ارور فایل - - - Extracting PKG %1/%2 - درحال استخراج PKG %1/%2 - - - Extraction Finished - استخراج به پایان رسید - - - Game successfully installed at %1 - بازی با موفقیت در %1 نصب شد - - - File doesn't appear to be a valid PKG file - این فایل یک PKG درست به نظر نمی آید - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - بازکردن پوشه - - - PKG ERROR - PKG ارور فایل - - - Name - نام - - - Serial - سریال - - - Installed - Installed - - - Size - اندازه - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - منطقه - - - Flags - Flags - - - Path - مسیر - - - File - فایل - - - Unknown - ناشناخته - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index cd880fb23..d07f82bd5 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -882,10 +882,6 @@ Error creating shortcut! Virhe pikakuvakkeen luonnissa! - - Install PKG - Asenna PKG - Game Peli @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Valitse hakemisto - - - Select which directory you want to install to. - Valitse, mihin hakemistoon haluat asentaa. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Avaa/Lisää Elf Hakemisto - - Install Packages (PKG) - Asenna Paketteja (PKG) - Boot Game Käynnistä Peli @@ -1234,10 +1207,6 @@ Configure... Asetukset... - - Install application from a .pkg file - Asenna sovellus .pkg tiedostosta - Recent Games Viimeisimmät Pelit @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Selain - Search... Hae... @@ -1426,70 +1391,6 @@ Only one file can be selected! Vain yksi tiedosto voi olla valittuna! - - PKG Extraction - PKG:n purku - - - Patch detected! - Päivitys havaittu! - - - PKG and Game versions match: - PKG- ja peliversiot vastaavat: - - - Would you like to overwrite? - Haluatko korvata? - - - PKG Version %1 is older than installed version: - PKG-versio %1 on vanhempi kuin asennettu versio: - - - Game is installed: - Peli on asennettu: - - - Would you like to install Patch: - Haluatko asentaa päivityksen: - - - DLC Installation - Lisäsisällön asennus - - - Would you like to install DLC: %1? - Haluatko asentaa lisäsisällön: %1? - - - DLC already installed: - Lisäsisältö on jo asennettu: - - - Game already installed - Peli on jo asennettu - - - PKG ERROR - PKG VIRHE - - - Extracting PKG %1/%2 - Purkaminen PKG %1/%2 - - - Extraction Finished - Purku valmis - - - Game successfully installed at %1 - Peli asennettu onnistuneesti kohtaan %1 - - - File doesn't appear to be a valid PKG file - Tiedosto ei vaikuta olevan kelvollinen PKG-tiedosto - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Avaa Hakemisto - - - PKG ERROR - PKG VIRHE - - - Name - Nimi - - - Serial - Sarjanumero - - - Installed - Installed - - - Size - Koko - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Alue - - - Flags - Flags - - - Path - Polku - - - File - Tiedosto - - - Unknown - Tuntematon - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index 1f7a726cd..89599e32c 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -882,10 +882,6 @@ Error creating shortcut! Erreur lors de la création du raccourci ! - - Install PKG - Installer un PKG - Game Jeu @@ -978,25 +974,6 @@ Raccourcis - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choisir un répertoire - - - Select which directory you want to install to. - Sélectionnez le répertoire où vous souhaitez effectuer l'installation. - - - Install All Queued to Selected Folder - Installer toute la file d’attente dans le dossier sélectionné - - - Delete PKG File on Install - Supprimer le fichier PKG à l'installation - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Ouvrir/Ajouter un dossier ELF - - Install Packages (PKG) - Installer des packages (PKG) - Boot Game Démarrer un jeu @@ -1234,10 +1207,6 @@ Configure... Configurer... - - Install application from a .pkg file - Installer une application depuis un fichier .pkg - Recent Games Jeux récents @@ -1308,15 +1277,11 @@ Trophy Viewer - Trophy Viewer + Visionneuse de trophées No games found. Please add your games to your library first. - No games found. Please add your games to your library first. - - - PKG Viewer - Visionneuse PKG + Aucun jeu trouvé. Veuillez d'abord ajouter vos jeux à votre bibliothèque. Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Un seul fichier peut être sélectionné ! - - PKG Extraction - Extraction du PKG - - - Patch detected! - Patch détecté ! - - - PKG and Game versions match: - Les versions PKG et jeu correspondent: - - - Would you like to overwrite? - Souhaitez-vous remplacer ? - - - PKG Version %1 is older than installed version: - La version PKG %1 est plus ancienne que la version installée: - - - Game is installed: - Jeu installé: - - - Would you like to install Patch: - Souhaitez-vous installer le patch: - - - DLC Installation - Installation du DLC - - - Would you like to install DLC: %1? - Souhaitez-vous installer le DLC: %1 ? - - - DLC already installed: - DLC déjà installé: - - - Game already installed - Jeu déjà installé - - - PKG ERROR - Erreur PKG - - - Extracting PKG %1/%2 - Extraction PKG %1/%2 - - - Extraction Finished - Extraction terminée - - - Game successfully installed at %1 - Jeu installé avec succès dans %1 - - - File doesn't appear to be a valid PKG file - Le fichier ne semble pas être un PKG valide - Run Game Lancer le jeu @@ -1498,14 +1399,6 @@ Eboot.bin file not found Fichier Eboot.bin introuvable - - PKG File (*.PKG *.pkg) - Fichier PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG est un patch ou DLC, veuillez d'abord installer le jeu ! - Game is already running! Le jeu est déjà en cours ! @@ -1516,110 +1409,43 @@ Play - Play + Jouer Pause - Pause + Pause Stop - Stop + Stop Restart - Restart + Redémarrer Full Screen - Full Screen + Plein écran Controllers - Controllers + Contrôleurs Keyboard - Keyboard + Clavier Refresh List - Refresh List + Rafraîchir la liste Resume - Resume + Reprendre Show Labels Under Icons - Show Labels Under Icons - - - - PKGViewer - - Open Folder - Ouvrir un dossier - - - PKG ERROR - Erreur PKG - - - Name - Nom - - - Serial - Numéro de série - - - Installed - Installé - - - Size - Taille - - - Category - Catégorie - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Région - - - Flags - Les indicateurs - - - Path - Répertoire - - - File - Fichier - - - Unknown - Inconnu - - - Package - Package + Afficher les libellés sous les icônes @@ -2241,7 +2067,7 @@ Select Game: - Select Game: + Sélectionnez un jeu: Progress diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 8746058b3..85b6f2c95 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -882,10 +882,6 @@ Error creating shortcut! Hiba a parancsikon létrehozásával! - - Install PKG - PKG telepítése - Game Játék @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Mappa kiválasztása - - - Select which directory you want to install to. - Válassza ki a mappát a játékok telepítésére. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder ELF Mappa Megnyitása/Hozzáadása - - Install Packages (PKG) - PKG-k Telepítése (PKG) - Boot Game Játék Indítása @@ -1234,10 +1207,6 @@ Configure... Konfigurálás... - - Install application from a .pkg file - Program telepítése egy .pkg fájlból - Recent Games Legutóbbi Játékok @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Nézegető - Search... Keresés... @@ -1426,70 +1391,6 @@ Only one file can be selected! Csak egy fájl választható ki! - - PKG Extraction - PKG kicsomagolás - - - Patch detected! - Frissítés észlelve! - - - PKG and Game versions match: - A PKG és a játék verziói egyeznek: - - - Would you like to overwrite? - Szeretné felülírni? - - - PKG Version %1 is older than installed version: - A(z) %1-es PKG verzió régebbi, mint a telepített verzió: - - - Game is installed: - A játék telepítve van: - - - Would you like to install Patch: - Szeretné telepíteni a frissítést: - - - DLC Installation - DLC Telepítés - - - Would you like to install DLC: %1? - Szeretné telepíteni a %1 DLC-t? - - - DLC already installed: - DLC már telepítve: - - - Game already installed - A játék már telepítve van - - - PKG ERROR - PKG HIBA - - - Extracting PKG %1/%2 - PKG kicsomagolása %1/%2 - - - Extraction Finished - Kicsomagolás befejezve - - - Game successfully installed at %1 - A játék sikeresen telepítve itt: %1 - - - File doesn't appear to be a valid PKG file - A fájl nem tűnik érvényes PKG fájlnak - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Mappa Megnyitása - - - PKG ERROR - PKG HIBA - - - Name - Név - - - Serial - Sorozatszám - - - Installed - Installed - - - Size - Méret - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Régió - - - Flags - Flags - - - Path - Útvonal - - - File - Fájl - - - Unknown - Ismeretlen - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index 5960715ae..1858da2a7 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -7,22 +7,22 @@ AboutDialog About shadPS4 - About shadPS4 + Tentang shadPS4 shadPS4 is an experimental open-source emulator for the PlayStation 4. - shadPS4 is an experimental open-source emulator for the PlayStation 4. + shadPS4 adalah emulator sumber terbuka eksperimental untuk PlayStation 4. This software should not be used to play games you have not legally obtained. - This software should not be used to play games you have not legally obtained. + Perangkat lunak ini tidak boleh digunakan untuk memainkan permainan yang tidak Anda peroleh secara legal. CheatsPatches Cheats / Patches for - Cheats / Patches for + Kecurangan / Tambalan untuk Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n @@ -34,7 +34,7 @@ Serial: - Serial: + Seri: Version: @@ -400,38 +400,38 @@ Playable - Playable + Dapat dimainkan ControlSettings Configure Controls - Configure Controls + Konfigurasi Kontrol D-Pad - D-Pad + Tombol arah Up - Up + Atas Left - Left + Kiri Right - Right + Kanan Down - Down + Bawah Left Stick Deadzone (def:2 max:127) - Left Stick Deadzone (def:2 max:127) + Zona Mati Stik Kiri (standar: 2, maksimum: 127) Left Deadzone @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Hanya satu file yang bisa dipilih! - - PKG Extraction - Ekstraksi PKG - - - Patch detected! - Patch terdeteksi! - - - PKG and Game versions match: - Versi PKG dan Game cocok: - - - Would you like to overwrite? - Apakah Anda ingin menimpa? - - - PKG Version %1 is older than installed version: - Versi PKG %1 lebih lama dari versi yang terpasang: - - - Game is installed: - Game telah terpasang: - - - Would you like to install Patch: - Apakah Anda ingin menginstal patch: - - - DLC Installation - Instalasi DLC - - - Would you like to install DLC: %1? - Apakah Anda ingin menginstal DLC: %1? - - - DLC already installed: - DLC sudah terpasang: - - - Game already installed - Game sudah terpasang - - - PKG ERROR - KESALAHAN PKG - - - Extracting PKG %1/%2 - Mengekstrak PKG %1/%2 - - - Extraction Finished - Ekstraksi Selesai - - - Game successfully installed at %1 - Game berhasil dipasang di %1 - - - File doesn't appear to be a valid PKG file - File tampaknya bukan file PKG yang valid - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - KESALAHAN PKG - - - Name - Nama - - - Serial - Serial - - - Installed - Installed - - - Size - Ukuran - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Wilayah - - - Flags - Flags - - - Path - Jalur - - - File - File - - - Unknown - Tidak Dikenal - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index 3e95e3df6..af321bd92 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -882,10 +882,6 @@ Error creating shortcut! Errore nella creazione della scorciatoia! - - Install PKG - Installa PKG - Game Gioco @@ -978,25 +974,6 @@ Associazioni dei pulsanti - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Scegli cartella - - - Select which directory you want to install to. - Seleziona in quale cartella vuoi effettuare l'installazione. - - - Install All Queued to Selected Folder - Installa tutto in coda nella Cartella Selezionata - - - Delete PKG File on Install - Elimina file PKG dopo Installazione - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Apri/Aggiungi cartella Elf - - Install Packages (PKG) - Installa Pacchetti (PKG) - Boot Game Avvia Gioco @@ -1234,10 +1207,6 @@ Configure... Configura... - - Install application from a .pkg file - Installa applicazione da un file .pkg - Recent Games Giochi Recenti @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Nessun gioco trovato. Aggiungi prima i tuoi giochi alla tua libreria. - - PKG Viewer - Visualizzatore PKG - Search... Cerca... @@ -1426,70 +1391,6 @@ Only one file can be selected! Si può selezionare solo un file! - - PKG Extraction - Estrazione file PKG - - - Patch detected! - Patch rilevata! - - - PKG and Game versions match: - Le versioni di PKG e del Gioco corrispondono: - - - Would you like to overwrite? - Vuoi sovrascrivere? - - - PKG Version %1 is older than installed version: - La versione PKG %1 è più vecchia rispetto alla versione installata: - - - Game is installed: - Gioco installato: - - - Would you like to install Patch: - Vuoi installare la patch: - - - DLC Installation - Installazione DLC - - - Would you like to install DLC: %1? - Vuoi installare il DLC: %1? - - - DLC already installed: - DLC già installato: - - - Game already installed - Gioco già installato - - - PKG ERROR - ERRORE PKG - - - Extracting PKG %1/%2 - Estrazione file PKG %1/%2 - - - Extraction Finished - Estrazione Completata - - - Game successfully installed at %1 - Gioco installato correttamente in %1 - - - File doesn't appear to be a valid PKG file - Il file sembra non essere un file PKG valido - Run Game Esegui Gioco @@ -1498,14 +1399,6 @@ Eboot.bin file not found File Eboot.bin non trovato - - PKG File (*.PKG *.pkg) - File PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - Il file PKG è una patch o DLC, si prega di installare prima il gioco! - Game is already running! Il gioco è già in esecuzione! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Apri Cartella - - - PKG ERROR - ERRORE PKG - - - Name - Nome - - - Serial - Seriale - - - Installed - Installato - - - Size - Dimensione - - - Category - Categoria - - - Type - Tipo - - - App Ver - Vers. App. - - - FW - FW - - - Region - Regione - - - Flags - Segnalazioni - - - Path - Percorso - - - File - File - - - Unknown - Sconosciuto - - - Package - Pacchetto - - SettingsDialog diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index 1aaa3fa7c..d4b8fa5b7 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -882,10 +882,6 @@ Error creating shortcut! ショートカットの作成に失敗しました! - - Install PKG - PKGをインストール - Game ゲーム @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - ディレクトリを選択 - - - Select which directory you want to install to. - インストール先のディレクトリを選択してください。 - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - インストール時にPKGファイルを削除 - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Elfフォルダを開く/追加する - - Install Packages (PKG) - パッケージをインストール (PKG) - Boot Game ゲームを起動 @@ -1234,10 +1207,6 @@ Configure... 設定... - - Install application from a .pkg file - .pkgファイルからアプリケーションをインストール - Recent Games 最近プレイしたゲーム @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKGビューアー - Search... 検索... @@ -1426,70 +1391,6 @@ Only one file can be selected! 1つのファイルしか選択できません! - - PKG Extraction - PKGの抽出 - - - Patch detected! - パッチが検出されました! - - - PKG and Game versions match: - PKGとゲームのバージョンが一致しています: - - - Would you like to overwrite? - 上書きしてもよろしいですか? - - - PKG Version %1 is older than installed version: - PKGバージョン %1 はインストールされているバージョンよりも古いです: - - - Game is installed: - ゲームはインストール済みです: - - - Would you like to install Patch: - パッチをインストールしてもよろしいですか: - - - DLC Installation - DLCのインストール - - - Would you like to install DLC: %1? - DLCをインストールしてもよろしいですか: %1? - - - DLC already installed: - DLCはすでにインストールされています: - - - Game already installed - ゲームはすでにインストールされています - - - PKG ERROR - PKGエラー - - - Extracting PKG %1/%2 - PKGを抽出中 %1/%2 - - - Extraction Finished - 抽出完了 - - - Game successfully installed at %1 - ゲームが %1 に正常にインストールされました - - - File doesn't appear to be a valid PKG file - ファイルが有効なPKGファイルでないようです - Run Game ゲームを実行 @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin ファイルが見つかりません - - PKG File (*.PKG *.pkg) - PKGファイル (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! ゲームは既に実行されています! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - フォルダーを開く - - - PKG ERROR - PKGエラー - - - Name - 名前 - - - Serial - シリアル - - - Installed - Installed - - - Size - サイズ - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - 地域 - - - Flags - Flags - - - Path - パス - - - File - ファイル - - - Unknown - 不明 - - - Package - パッケージ - - SettingsDialog diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 9dd06028d..9d4b58c79 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Only one file can be selected! - - PKG Extraction - PKG Extraction - - - Patch detected! - Patch detected! - - - PKG and Game versions match: - PKG and Game versions match: - - - Would you like to overwrite? - Would you like to overwrite? - - - PKG Version %1 is older than installed version: - PKG Version %1 is older than installed version: - - - Game is installed: - Game is installed: - - - Would you like to install Patch: - Would you like to install Patch: - - - DLC Installation - DLC Installation - - - Would you like to install DLC: %1? - Would you like to install DLC: %1? - - - DLC already installed: - DLC already installed: - - - Game already installed - Game already installed - - - PKG ERROR - PKG ERROR - - - Extracting PKG %1/%2 - Extracting PKG %1/%2 - - - Extraction Finished - Extraction Finished - - - Game successfully installed at %1 - Game successfully installed at %1 - - - File doesn't appear to be a valid PKG file - File doesn't appear to be a valid PKG file - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - PKG ERROR - - - Name - Name - - - Serial - Serial - - - Installed - Installed - - - Size - Size - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Region - - - Flags - Flags - - - Path - Path - - - File - File - - - Unknown - 알 수 없음 - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 6e98ddc45..55a9fac0c 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Žaidimas @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Galite pasirinkti tik vieną failą! - - PKG Extraction - PKG ištraukimas - - - Patch detected! - Rasta atnaujinimą! - - - PKG and Game versions match: - PKG ir žaidimo versijos sutampa: - - - Would you like to overwrite? - Ar norite perrašyti? - - - PKG Version %1 is older than installed version: - PKG versija %1 yra senesnė nei įdiegta versija: - - - Game is installed: - Žaidimas įdiegtas: - - - Would you like to install Patch: - Ar norite įdiegti atnaujinimą: - - - DLC Installation - DLC diegimas - - - Would you like to install DLC: %1? - Ar norite įdiegti DLC: %1? - - - DLC already installed: - DLC jau įdiegtas: - - - Game already installed - Žaidimas jau įdiegtas - - - PKG ERROR - PKG KLAIDA - - - Extracting PKG %1/%2 - Ekstrakcinis PKG %1/%2 - - - Extraction Finished - Ekstrakcija baigta - - - Game successfully installed at %1 - Žaidimas sėkmingai įdiegtas %1 - - - File doesn't appear to be a valid PKG file - Failas atrodo, kad nėra galiojantis PKG failas - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - PKG KLAIDA - - - Name - Vardas - - - Serial - Serijinis numeris - - - Installed - Installed - - - Size - Dydis - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Regionas - - - Flags - Flags - - - Path - Kelias - - - File - File - - - Unknown - Nežinoma - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index 6faff415e..4a0835c1c 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -882,10 +882,6 @@ Error creating shortcut! Feil ved opprettelse av snarvei! - - Install PKG - Installer PKG - Game Spill @@ -978,25 +974,6 @@ Hurtigtast - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Velg mappe - - - Select which directory you want to install to. - Velg hvilken mappe du vil installere til. - - - Install All Queued to Selected Folder - Installer alle i kø til den valgte mappa - - - Delete PKG File on Install - Slett PKG-fila ved installering - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Åpne eller legg til Elf-mappe - - Install Packages (PKG) - Installer pakker (PKG) - Boot Game Start spill @@ -1234,10 +1207,6 @@ Configure... Sett opp … - - Install application from a .pkg file - Installer fra en .pkg fil - Recent Games Nylige spill @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Fant ingen spill. Legg til spillene dine i biblioteket først. - - PKG Viewer - PKG-viser - Search... Søk … @@ -1426,70 +1391,6 @@ Only one file can be selected! Kun én fil kan velges! - - PKG Extraction - PKG-utpakking - - - Patch detected! - Programrettelse oppdaget! - - - PKG and Game versions match: - PKG og spillversjoner stemmer overens: - - - Would you like to overwrite? - Ønsker du å overskrive? - - - PKG Version %1 is older than installed version: - PKG-versjon %1 er eldre enn installert versjon: - - - Game is installed: - Spillet er installert: - - - Would you like to install Patch: - Ønsker du å installere programrettelsen: - - - DLC Installation - DLC installasjon - - - Would you like to install DLC: %1? - Ønsker du å installere DLC: %1? - - - DLC already installed: - DLC allerede installert: - - - Game already installed - Spillet er allerede installert - - - PKG ERROR - PKG FEIL - - - Extracting PKG %1/%2 - Pakker ut PKG %1/%2 - - - Extraction Finished - Utpakking fullført - - - Game successfully installed at %1 - Spillet ble installert i %1 - - - File doesn't appear to be a valid PKG file - Fila ser ikke ut til å være en gyldig PKG-fil - Run Game Kjør spill @@ -1498,14 +1399,6 @@ Eboot.bin file not found Klarte ikke finne Eboot.bin-fila - - PKG File (*.PKG *.pkg) - PKG-fil (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG er en programrettelse eller DLC. Installer spillet først! - Game is already running! Spillet kjører allerede! @@ -1516,110 +1409,43 @@ Play - Play + Spill Pause - Pause + Pause Stop - Stop + Stopp Restart - Restart + Start på nytt Full Screen - Full Screen + Fullskjerm Controllers - Controllers + Kontroller Keyboard - Keyboard + Tastatur Refresh List - Refresh List + Oppdater lista Resume - Resume + Gjenoppta Show Labels Under Icons - Show Labels Under Icons - - - - PKGViewer - - Open Folder - Åpne mappe - - - PKG ERROR - PKG FEIL - - - Name - Navn - - - Serial - Serienummer - - - Installed - Installert - - - Size - Størrelse - - - Category - Kategori - - - Type - Type - - - App Ver - Programversjon - - - FW - FV - - - Region - Region - - - Flags - Flagg - - - Path - Adresse - - - File - Fil - - - Unknown - Ukjent - - - Package - Pakke + Vis merkelapp under ikoner diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index 376eea5ef..918da6a67 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Je kunt slechts één bestand selecteren! - - PKG Extraction - PKG-extractie - - - Patch detected! - Patch gedetecteerd! - - - PKG and Game versions match: - PKG- en gameversies komen overeen: - - - Would you like to overwrite? - Wilt u overschrijven? - - - PKG Version %1 is older than installed version: - PKG-versie %1 is ouder dan de geïnstalleerde versie: - - - Game is installed: - Game is geïnstalleerd: - - - Would you like to install Patch: - Wilt u de patch installeren: - - - DLC Installation - DLC-installatie - - - Would you like to install DLC: %1? - Wilt u DLC installeren: %1? - - - DLC already installed: - DLC al geïnstalleerd: - - - Game already installed - Game al geïnstalleerd - - - PKG ERROR - PKG FOUT - - - Extracting PKG %1/%2 - PKG %1/%2 aan het extraheren - - - Extraction Finished - Extractie voltooid - - - Game successfully installed at %1 - Spel succesvol geïnstalleerd op %1 - - - File doesn't appear to be a valid PKG file - Het bestand lijkt geen geldig PKG-bestand te zijn - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - PKG FOUT - - - Name - Naam - - - Serial - Serienummer - - - Installed - Installed - - - Size - Grootte - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Regio - - - Flags - Flags - - - Path - Pad - - - File - File - - - Unknown - Onbekend - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index a77b43e09..39950d6ec 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -882,10 +882,6 @@ Error creating shortcut! Utworzenie skrótu zakończone niepowodzeniem! - - Install PKG - Zainstaluj PKG - Game Gra @@ -978,25 +974,6 @@ Przypisanie klawiszy - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Wybierz katalog - - - Select which directory you want to install to. - Wybierz katalog, do którego chcesz zainstalować. - - - Install All Queued to Selected Folder - Zainstaluj wszystkie oczekujące do wybranego folderu - - - Delete PKG File on Install - Usuń plik PKG po instalacji - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Otwórz/Dodaj folder Elf - - Install Packages (PKG) - Zainstaluj paczkę (PKG) - Boot Game Uruchom grę @@ -1234,10 +1207,6 @@ Configure... Konfiguruj... - - Install application from a .pkg file - Zainstaluj aplikacje z pliku .pkg - Recent Games Ostatnie gry @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Nie znaleziono gier. Najpierw dodaj swoje gry do swojej biblioteki. - - PKG Viewer - Menedżer plików PKG - Search... Szukaj... @@ -1426,70 +1391,6 @@ Only one file can be selected! Można wybrać tylko jeden plik! - - PKG Extraction - Wypakowywanie PKG - - - Patch detected! - Wykryto łatkę! - - - PKG and Game versions match: - Wersje PKG i gry są zgodne: - - - Would you like to overwrite? - Czy chcesz nadpisać? - - - PKG Version %1 is older than installed version: - Wersja PKG %1 jest starsza niż zainstalowana wersja: - - - Game is installed: - Gra jest zainstalowana: - - - Would you like to install Patch: - Czy chcesz zainstalować łatkę: - - - DLC Installation - Instalacja dodatkowej zawartości (DLC) - - - Would you like to install DLC: %1? - Czy chcesz zainstalować dodatkową zawartość (DLC): %1? - - - DLC already installed: - Dodatkowa zawartość (DLC) już zainstalowana: - - - Game already installed - Gra już zainstalowana - - - PKG ERROR - BŁĄD PKG - - - Extracting PKG %1/%2 - Wypakowywanie PKG %1/%2 - - - Extraction Finished - Wypakowywanie zakończone - - - Game successfully installed at %1 - Gra pomyślnie zainstalowana w %1 - - - File doesn't appear to be a valid PKG file - Plik nie wydaje się być prawidłowym plikiem PKG - Run Game Uruchom grę @@ -1498,14 +1399,6 @@ Eboot.bin file not found Nie znaleziono pliku EBOOT.BIN - - PKG File (*.PKG *.pkg) - Plik PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG jest aktualizacją lub dodatkową zawartością (DLC), najpierw zainstaluj grę! - Game is already running! Gra jest już uruchomiona! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Otwórz folder - - - PKG ERROR - BŁĄD PKG - - - Name - Nazwa - - - Serial - Numer seryjny - - - Installed - Zainstalowano - - - Size - Rozmiar - - - Category - Kategoria - - - Type - Typ - - - App Ver - Wersja aplikacji - - - FW - Oprogramowanie - - - Region - Region - - - Flags - Flagi - - - Path - Ścieżka - - - File - Plik - - - Unknown - Nieznany - - - Package - Paczka - - SettingsDialog diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index ea82086f3..5d1582b5a 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -882,10 +882,6 @@ Error creating shortcut! Erro ao criar atalho! - - Install PKG - Instalar PKG - Game Jogo @@ -978,25 +974,6 @@ Teclas de atalho - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Escolha o diretório - - - Select which directory you want to install to. - Selecione o diretório em que você deseja instalar. - - - Install All Queued to Selected Folder - Instalar Tudo da Fila para a Pasta Selecionada - - - Delete PKG File on Install - Excluir o PKG após a Instalação - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Abrir/Adicionar pasta Elf - - Install Packages (PKG) - Instalar Pacotes (PKG) - Boot Game Iniciar Jogo @@ -1234,10 +1207,6 @@ Configure... Configurar... - - Install application from a .pkg file - Instalar aplicativo de um arquivo .pkg - Recent Games Jogos Recentes @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Nenhum jogo encontrado. Adicione seus jogos à sua biblioteca primeiro. - - PKG Viewer - Visualizador de PKG - Search... Pesquisar... @@ -1426,70 +1391,6 @@ Only one file can be selected! Apenas um arquivo pode ser selecionado! - - PKG Extraction - Extração de PKG - - - Patch detected! - Atualização detectada! - - - PKG and Game versions match: - As versões do PKG e do Jogo são iguais: - - - Would you like to overwrite? - Você gostaria de sobrescrever? - - - PKG Version %1 is older than installed version: - A Versão do PKG %1 é mais antiga do que a versão instalada: - - - Game is installed: - Jogo instalado: - - - Would you like to install Patch: - Você gostaria de instalar a atualização: - - - DLC Installation - Instalação de DLC - - - Would you like to install DLC: %1? - Você gostaria de instalar o DLC: %1? - - - DLC already installed: - DLC já está instalado: - - - Game already installed - O jogo já está instalado: - - - PKG ERROR - ERRO DE PKG - - - Extracting PKG %1/%2 - Extraindo PKG %1/%2 - - - Extraction Finished - Extração Concluída - - - Game successfully installed at %1 - Jogo instalado com sucesso em %1 - - - File doesn't appear to be a valid PKG file - O arquivo não parece ser um arquivo PKG válido - Run Game Executar Jogo @@ -1498,14 +1399,6 @@ Eboot.bin file not found Arquivo Eboot.bin não encontrado - - PKG File (*.PKG *.pkg) - Arquivo PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - O PKG é um patch ou DLC, por favor instale o jogo primeiro! - Game is already running! O jogo já está executando! @@ -1555,73 +1448,6 @@ Mostrar Rótulos Sob Ícones - - PKGViewer - - Open Folder - Abrir Pasta - - - PKG ERROR - ERRO DE PKG - - - Name - Nome - - - Serial - Serial - - - Installed - Instalado - - - Size - Tamanho - - - Category - Categoria - - - Type - Tipo - - - App Ver - Versão do App - - - FW - FW - - - Region - Região - - - Flags - Flags - - - Path - Caminho - - - File - Arquivo - - - Unknown - Desconhecido - - - Package - Pacote - - SettingsDialog diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index 7ca3eebb5..a155a6324 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -882,10 +882,6 @@ Error creating shortcut! Erro ao criar atalho! - - Install PKG - Instalar PKG - Game Jogo @@ -978,25 +974,6 @@ Combinações de Teclas - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Escolher diretório - - - Select which directory you want to install to. - Selecione o diretório em que deseja instalar. - - - Install All Queued to Selected Folder - Instalar Todos os Pendentes para a Pasta Selecionada - - - Delete PKG File on Install - Eliminar Ficheiro PKG após Instalação - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Abrir/Adicionar pasta Elf - - Install Packages (PKG) - Instalar Pacotes (PKG) - Boot Game Iniciar Jogo @@ -1234,10 +1207,6 @@ Configure... Configurar... - - Install application from a .pkg file - Instalar aplicação através de um ficheiro .pkg - Recent Games Jogos Recentes @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Nenhum jogo encontrado. Por favor, adicione os seus jogos à sua biblioteca primeiro. - - PKG Viewer - Visualizador PKG - Search... Procurar... @@ -1426,70 +1391,6 @@ Only one file can be selected! Apenas um ficheiro pode ser selecionado! - - PKG Extraction - Extração de PKG - - - Patch detected! - Patch detetado! - - - PKG and Game versions match: - As versões do PKG e do Jogo coincidem: - - - Would you like to overwrite? - Gostaria de substituir? - - - PKG Version %1 is older than installed version: - A versão do PKG %1 é mais antiga do que a versão instalada: - - - Game is installed: - O jogo está instalado: - - - Would you like to install Patch: - Gostaria de instalar o Patch: - - - DLC Installation - Instalação de DLC - - - Would you like to install DLC: %1? - Deseja instalar o DLC: %1? - - - DLC already installed: - DLC já está instalado: - - - Game already installed - O jogo já está instalado - - - PKG ERROR - ERRO PKG - - - Extracting PKG %1/%2 - A extrair PKG %1/%2 - - - Extraction Finished - Extração Finalizada - - - Game successfully installed at %1 - Jogo instalado com sucesso em %1 - - - File doesn't appear to be a valid PKG file - O ficheiro não aparenta ser um ficheiro PKG válido - Run Game Executar Jogo @@ -1498,14 +1399,6 @@ Eboot.bin file not found Ficheiro eboot.bin não encontrado - - PKG File (*.PKG *.pkg) - Ficheiro PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - Este PKG é um patch ou DLC, por favor instale o respetivo jogo primeiro! - Game is already running! O jogo já está a ser executado! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Abrir Pasta - - - PKG ERROR - ERRO PKG - - - Name - Nome - - - Serial - Número de Série - - - Installed - Instalado - - - Size - Tamanho - - - Category - Categoria - - - Type - Tipo - - - App Ver - App Ver - - - FW - FW - - - Region - Região - - - Flags - Flags - - - Path - Caminho - - - File - Ficheiro - - - Unknown - Desconhecido - - - Package - Pacote - - SettingsDialog diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 6e008ac20..18121a204 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Numai un fișier poate fi selectat! - - PKG Extraction - Extracție PKG - - - Patch detected! - Patch detectat! - - - PKG and Game versions match: - Versiunile PKG și ale jocului sunt compatibile: - - - Would you like to overwrite? - Doriți să suprascrieți? - - - PKG Version %1 is older than installed version: - Versiunea PKG %1 este mai veche decât versiunea instalată: - - - Game is installed: - Jocul este instalat: - - - Would you like to install Patch: - Doriți să instalați patch-ul: - - - DLC Installation - Instalare DLC - - - Would you like to install DLC: %1? - Doriți să instalați DLC-ul: %1? - - - DLC already installed: - DLC deja instalat: - - - Game already installed - Jocul deja instalat - - - PKG ERROR - EROARE PKG - - - Extracting PKG %1/%2 - Extracție PKG %1/%2 - - - Extraction Finished - Extracție terminată - - - Game successfully installed at %1 - Jocul a fost instalat cu succes la %1 - - - File doesn't appear to be a valid PKG file - Fișierul nu pare să fie un fișier PKG valid - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Deschide Folder - - - PKG ERROR - EROARE PKG - - - Name - Nume - - - Serial - Serie - - - Installed - Installed - - - Size - Dimensiune - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Regiune - - - Flags - Flags - - - Path - Drum - - - File - File - - - Unknown - Necunoscut - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 560c8c110..3944af589 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -882,10 +882,6 @@ Error creating shortcut! Ошибка создания ярлыка! - - Install PKG - Установить PKG - Game Игры @@ -978,25 +974,6 @@ Бинды клавиш - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Выберите папку - - - Select which directory you want to install to. - Выберите папку, в которую вы хотите установить. - - - Install All Queued to Selected Folder - Установить все из очереди в выбранную папку - - - Delete PKG File on Install - Удалить файл PKG при установке - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Открыть/Добавить папку Elf - - Install Packages (PKG) - Установить пакеты (PKG) - Boot Game Запустить игру @@ -1234,10 +1207,6 @@ Configure... Настроить... - - Install application from a .pkg file - Установить приложение из файла .pkg - Recent Games Недавние игры @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Не найдено ни одной игры. Пожалуйста, сначала добавьте игры в библиотеку. - - PKG Viewer - Просмотр PKG - Search... Поиск... @@ -1426,70 +1391,6 @@ Only one file can be selected! Можно выбрать только один файл! - - PKG Extraction - Извлечение PKG - - - Patch detected! - Обнаружен патч! - - - PKG and Game versions match: - Версии PKG и игры совпадают: - - - Would you like to overwrite? - Хотите перезаписать? - - - PKG Version %1 is older than installed version: - Версия PKG %1 старше установленной версии: - - - Game is installed: - Игра установлена: - - - Would you like to install Patch: - Хотите установить патч: - - - DLC Installation - Установка DLC - - - Would you like to install DLC: %1? - Вы хотите установить DLC: %1? - - - DLC already installed: - DLC уже установлен: - - - Game already installed - Игра уже установлена - - - PKG ERROR - ОШИБКА PKG - - - Extracting PKG %1/%2 - Извлечение PKG %1/%2 - - - Extraction Finished - Извлечение завершено - - - Game successfully installed at %1 - Игра успешно установлена в %1 - - - File doesn't appear to be a valid PKG file - Файл не является допустимым файлом PKG - Run Game Запустить игру @@ -1498,14 +1399,6 @@ Eboot.bin file not found Файл eboot.bin не найден - - PKG File (*.PKG *.pkg) - Файл PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - Выбранный PKG является патчем или DLC, пожалуйста, сначала установите игру! - Game is already running! Игра уже запущена! @@ -1555,73 +1448,6 @@ Показывать метки под значками - - PKGViewer - - Open Folder - Открыть папку - - - PKG ERROR - ОШИБКА PKG - - - Name - Название - - - Serial - Серийный номер - - - Installed - Установлено - - - Size - Размер - - - Category - Категория - - - Type - Тип - - - App Ver - Версия приложения - - - FW - Прошивка - - - Region - Регион - - - Flags - Флаги - - - Path - Путь - - - File - Файл - - - Unknown - Неизвестно - - - Package - Пакет - - SettingsDialog diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index 8a2c34c60..d59fd8c3e 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -882,10 +882,6 @@ Error creating shortcut! Gabim në krijimin e shkurtores! - - Install PKG - Instalo PKG - Game Loja @@ -978,25 +974,6 @@ Caktimet e Tasteve - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Përzgjidh dosjen - - - Select which directory you want to install to. - Përzgjidh në cilën dosje do që të instalosh. - - - Install All Queued to Selected Folder - Instalo të gjitha të radhiturat në dosjen e zgjedhur - - - Delete PKG File on Install - Fshi skedarin PKG pas instalimit - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Hap/Shto Dosje ELF - - Install Packages (PKG) - Instalo Paketat (PKG) - Boot Game Nis Lojën @@ -1234,10 +1207,6 @@ Configure... Konfiguro... - - Install application from a .pkg file - Instalo aplikacionin nga një skedar .pkg - Recent Games Lojërat e fundit @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Nuk u gjetën lojëra. Shto lojërat në librarinë tënde fillimisht. - - PKG Viewer - Shikuesi i PKG - Search... Kërko... @@ -1426,70 +1391,6 @@ Only one file can be selected! Mund të përzgjidhet vetëm një skedar! - - PKG Extraction - Nxjerrja e PKG-së - - - Patch detected! - U zbulua një arnë! - - - PKG and Game versions match: - PKG-ja dhe versioni i Lojës përputhen: - - - Would you like to overwrite? - Dëshiron të mbishkruash? - - - PKG Version %1 is older than installed version: - Versioni %1 i PKG-së është më i vjetër se versioni i instaluar: - - - Game is installed: - Loja është instaluar: - - - Would you like to install Patch: - Dëshiron të instalosh Arnën: - - - DLC Installation - Instalimi i DLC-ve - - - Would you like to install DLC: %1? - Dëshiron të instalosh DLC-në: %1? - - - DLC already installed: - DLC-ja është instaluar tashmë: - - - Game already installed - Loja është instaluar tashmë - - - PKG ERROR - GABIM PKG - - - Extracting PKG %1/%2 - Po nxirret PKG-ja %1/%2 - - - Extraction Finished - Nxjerrja Përfundoi - - - Game successfully installed at %1 - Loja u instalua me sukses në %1 - - - File doesn't appear to be a valid PKG file - Skedari nuk duket si skedar PKG i vlefshëm - Run Game Ekzekuto lojën @@ -1498,14 +1399,6 @@ Eboot.bin file not found Skedari Eboot.bin nuk u gjet - - PKG File (*.PKG *.pkg) - Skedar PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG-ja është një arnë ose DLC, të lutem instalo lojën fillimisht! - Game is already running! Loja tashmë është duke u ekzekutuar! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Hap Dosjen - - - PKG ERROR - GABIM PKG - - - Name - Emri - - - Serial - Seriku - - - Installed - Instaluar - - - Size - Madhësia - - - Category - Kategoria - - - Type - Lloji - - - App Ver - Versioni i aplikacionit - - - FW - Firmueri - - - Region - Rajoni - - - Flags - Flamurët - - - Path - Shtegu - - - File - Skedari - - - Unknown - E panjohur - - - Package - Paketa - - SettingsDialog diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index 91b544e05..b570c420e 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -882,10 +882,6 @@ Error creating shortcut! Fel vid skapandet av genväg! - - Install PKG - Installera PKG - Game Spel @@ -978,25 +974,6 @@ Tangentbindningar - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Välj katalog - - - Select which directory you want to install to. - Välj vilken katalog som du vill installera till. - - - Install All Queued to Selected Folder - Installera alla köade till markerad mapp - - - Delete PKG File on Install - Ta bort PKG-fil efter installation - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Öppna/Lägg till Elf-mapp - - Install Packages (PKG) - Installera paket (PKG) - Boot Game Starta spel @@ -1234,10 +1207,6 @@ Configure... Konfigurera... - - Install application from a .pkg file - Installera program från en .pkg-fil - Recent Games Senaste spel @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Inga spel hittades. Lägg till dina spel till biblioteket först. - - PKG Viewer - PKG-visare - Search... Sök... @@ -1426,70 +1391,6 @@ Only one file can be selected! Endast en fil kan väljas! - - PKG Extraction - PKG-extrahering - - - Patch detected! - Patch upptäcktes! - - - PKG and Game versions match: - PKG och spelversioner matchar: - - - Would you like to overwrite? - Vill du skriva över? - - - PKG Version %1 is older than installed version: - PKG-versionen %1 är äldre än installerad version: - - - Game is installed: - Spelet är installerat: - - - Would you like to install Patch: - Vill du installera patch: - - - DLC Installation - DLC-installation - - - Would you like to install DLC: %1? - Vill du installera DLC: %1? - - - DLC already installed: - DLC redan installerat: - - - Game already installed - Spelet redan installerat - - - PKG ERROR - PKG-FEL - - - Extracting PKG %1/%2 - Extraherar PKG %1/%2 - - - Extraction Finished - Extrahering färdig - - - Game successfully installed at %1 - Spelet installerades i %1 - - - File doesn't appear to be a valid PKG file - Filen verkar inte vara en giltig PKG-fil - Run Game Kör spel @@ -1498,14 +1399,6 @@ Eboot.bin file not found Filen eboot.bin hittades inte - - PKG File (*.PKG *.pkg) - PKG-fil (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG är en patch eller DLC. Installera spelet först! - Game is already running! Spelet är redan igång! @@ -1516,110 +1409,43 @@ Play - Play + Spela Pause - Pause + Paus Stop - Stop + Stoppa Restart - Restart + Starta om Full Screen - Full Screen + Helskärm Controllers - Controllers + Kontroller Keyboard - Keyboard + Tangentbord Refresh List - Refresh List + Uppdatera lista Resume - Resume + Återuppta Show Labels Under Icons - Show Labels Under Icons - - - - PKGViewer - - Open Folder - Öppna mapp - - - PKG ERROR - PKG-FEL - - - Name - Namn - - - Serial - Serienummer - - - Installed - Installerat - - - Size - Storlek - - - Category - Kategori - - - Type - Typ - - - App Ver - Appver - - - FW - FW - - - Region - Region - - - Flags - Flaggor - - - Path - Sökväg - - - File - Arkiv - - - Unknown - Okänt - - - Package - Paket + Visa etiketter under ikoner diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 4946874c9..d2d018116 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -882,10 +882,6 @@ Error creating shortcut! Kısayol oluşturulurken hata oluştu! - - Install PKG - PKG Yükle - Game Oyun @@ -978,25 +974,6 @@ Tuş Atamaları - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Klasörü Seç - - - Select which directory you want to install to. - Hangi dizine yüklemek istediğinizi seçin. - - - Install All Queued to Selected Folder - Tüm Kuyruktakileri Seçili Klasöre Yükle - - - Delete PKG File on Install - Yüklemede PKG Dosyasını Sil - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Elf Klasörü Aç/Ekle - - Install Packages (PKG) - Paketleri Kur (PKG) - Boot Game Oyunu Başlat @@ -1234,10 +1207,6 @@ Configure... Yapılandır... - - Install application from a .pkg file - .pkg dosyasından uygulama yükle - Recent Games Son Oyunlar @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. Oyun bulunamadı. Oyunlarınızı lütfen önce kütüphanenize ekleyin. - - PKG Viewer - PKG Görüntüleyici - Search... Ara... @@ -1426,70 +1391,6 @@ Only one file can be selected! Sadece bir dosya seçilebilir! - - PKG Extraction - PKG Çıkartma - - - Patch detected! - Yama tespit edildi! - - - PKG and Game versions match: - PKG ve oyun sürümleri uyumlu: - - - Would you like to overwrite? - Üzerine yazmak ister misiniz? - - - PKG Version %1 is older than installed version: - PKG Sürümü %1, kurulu sürümden daha eski: - - - Game is installed: - Oyun yüklendi: - - - Would you like to install Patch: - Yamanın yüklenmesini ister misiniz: - - - DLC Installation - DLC Yükleme - - - Would you like to install DLC: %1? - DLC'yi yüklemek ister misiniz: %1? - - - DLC already installed: - DLC zaten yüklü: - - - Game already installed - Oyun zaten yüklü - - - PKG ERROR - PKG HATASI - - - Extracting PKG %1/%2 - PKG Çıkarılıyor %1/%2 - - - Extraction Finished - Çıkarma Tamamlandı - - - Game successfully installed at %1 - Oyun başarıyla %1 konumuna yüklendi - - - File doesn't appear to be a valid PKG file - Dosya geçerli bir PKG dosyası gibi görünmüyor - Run Game Oyunu Çalıştır @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin dosyası bulunamadı - - PKG File (*.PKG *.pkg) - PKG Dosyası (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG bir yama ya da DLC, lütfen önce oyunu yükleyin! - Game is already running! Oyun zaten çalışıyor! @@ -1555,73 +1448,6 @@ Simgelerin Altında Etiketleri Göster - - PKGViewer - - Open Folder - Klasörü Aç - - - PKG ERROR - PKG HATASI - - - Name - Ad - - - Serial - Seri Numarası - - - Installed - Yüklü - - - Size - Boyut - - - Category - Kategori - - - Type - Tür - - - App Ver - Uygulama Sürümü - - - FW - Sistem Yazılımı - - - Region - Bölge - - - Flags - Bayraklar - - - Path - Yol - - - File - Dosya - - - Unknown - Bilinmeyen - - - Package - Paket - - SettingsDialog diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 69e6c5fc7..890fa163e 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -882,10 +882,6 @@ Error creating shortcut! Помилка при створенні ярлика! - - Install PKG - Встановити PKG - Game гри @@ -978,25 +974,6 @@ Призначення клавіш - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Виберіть папку - - - Select which directory you want to install to. - Виберіть папку, до якої ви хочете встановити. - - - Install All Queued to Selected Folder - Встановити все з черги до вибраної папки - - - Delete PKG File on Install - Видалити файл PKG під час встановлення - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Відкрити/Додати папку Elf - - Install Packages (PKG) - Встановити пакети (PKG) - Boot Game Запустити гру @@ -1234,10 +1207,6 @@ Configure... Налаштувати... - - Install application from a .pkg file - Встановити додаток з файлу .pkg - Recent Games Нещодавні ігри @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - Перегляд PKG - Search... Пошук... @@ -1426,70 +1391,6 @@ Only one file can be selected! Можна вибрати лише один файл! - - PKG Extraction - Розпакування PKG - - - Patch detected! - Виявлено патч! - - - PKG and Game versions match: - Версії PKG та гри збігаються: - - - Would you like to overwrite? - Бажаєте перезаписати? - - - PKG Version %1 is older than installed version: - Версія PKG %1 старіша за встановлену версію: - - - Game is installed: - Встановлена гра: - - - Would you like to install Patch: - Бажаєте встановити патч: - - - DLC Installation - Встановлення DLC - - - Would you like to install DLC: %1? - Ви бажаєте встановити DLC: %1? - - - DLC already installed: - DLC вже встановлено: - - - Game already installed - Гра вже встановлена - - - PKG ERROR - ПОМИЛКА PKG - - - Extracting PKG %1/%2 - Витягування PKG %1/%2 - - - Extraction Finished - Розпакування завершено - - - Game successfully installed at %1 - Гру успішно встановлено у %1 - - - File doesn't appear to be a valid PKG file - Файл не є дійсним PKG-файлом - Run Game Запустити гру @@ -1498,14 +1399,6 @@ Eboot.bin file not found Файл Boot.bin не знайдено - - PKG File (*.PKG *.pkg) - Файл PKG (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG - це патч або DLC, будь ласка, спочатку встановіть гру! - Game is already running! Гра вже запущена! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Відкрити папку - - - PKG ERROR - ПОМИЛКА PKG - - - Name - Назва - - - Serial - Серійний номер - - - Installed - Встановлені - - - Size - Розмір - - - Category - Категорія - - - Type - Тип - - - App Ver - Версія додатку - - - FW - ПЗ - - - Region - Регіон - - - Flags - Мітки - - - Path - Шлях - - - File - Файл - - - Unknown - Невідомо - - - Package - Пакет - - SettingsDialog diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 14bd29896..b8c1759be 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! Chỉ có thể chọn một tệp duy nhất! - - PKG Extraction - Giải nén PKG - - - Patch detected! - Đã phát hiện bản vá! - - - PKG and Game versions match: - Các phiên bản PKG và trò chơi khớp nhau: - - - Would you like to overwrite? - Bạn có muốn ghi đè không? - - - PKG Version %1 is older than installed version: - Phiên bản PKG %1 cũ hơn phiên bản đã cài đặt: - - - Game is installed: - Trò chơi đã được cài đặt: - - - Would you like to install Patch: - Bạn có muốn cài đặt bản vá: - - - DLC Installation - Cài đặt DLC - - - Would you like to install DLC: %1? - Bạn có muốn cài đặt DLC: %1? - - - DLC already installed: - DLC đã được cài đặt: - - - Game already installed - Trò chơi đã được cài đặt - - - PKG ERROR - LOI PKG - - - Extracting PKG %1/%2 - Đang giải nén PKG %1/%2 - - - Extraction Finished - Giải nén hoàn tất - - - Game successfully installed at %1 - Trò chơi đã được cài đặt thành công tại %1 - - - File doesn't appear to be a valid PKG file - Tệp không có vẻ là tệp PKG hợp lệ - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - LOI PKG - - - Name - Tên - - - Serial - Số seri - - - Installed - Installed - - - Size - Kích thước - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - Khu vực - - - Flags - Flags - - - Path - Đường dẫn - - - File - File - - - Unknown - Không xác định - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 7536b7d17..7d414a493 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -882,10 +882,6 @@ Error creating shortcut! 创建快捷方式出错! - - Install PKG - 安装 PKG - Game 游戏 @@ -978,25 +974,6 @@ 按键绑定 - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - 选择文件目录 - - - Select which directory you want to install to. - 选择您想要安装到的目录。 - - - Install All Queued to Selected Folder - 安装所有 PKG 到选定的文件夹 - - - Delete PKG File on Install - 安装后删除 PKG 文件 - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder 打开/添加 Elf 文件夹 - - Install Packages (PKG) - 安装 Packages (PKG) - Boot Game 启动游戏 @@ -1234,10 +1207,6 @@ Configure... 设置... - - Install application from a .pkg file - 从 .pkg 文件安装应用程序 - Recent Games 最近启动的游戏 @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. 未找到游戏。请先将您的游戏添加到您的资料库。 - - PKG Viewer - PKG 查看器 - Search... 搜索... @@ -1426,70 +1391,6 @@ Only one file can be selected! 只能选择一个文件! - - PKG Extraction - PKG 解压 - - - Patch detected! - 检测到补丁! - - - PKG and Game versions match: - PKG 和游戏版本匹配: - - - Would you like to overwrite? - 您想要覆盖吗? - - - PKG Version %1 is older than installed version: - PKG 版本 %1 比已安装版本更旧: - - - Game is installed: - 游戏已安装: - - - Would you like to install Patch: - 您想安装补丁吗: - - - DLC Installation - DLC 安装 - - - Would you like to install DLC: %1? - 您想安装 DLC:%1 吗? - - - DLC already installed: - DLC 已经安装: - - - Game already installed - 游戏已经安装 - - - PKG ERROR - PKG 错误 - - - Extracting PKG %1/%2 - 正在解压 PKG %1/%2 - - - Extraction Finished - 解压完成 - - - Game successfully installed at %1 - 游戏成功安装在 %1 - - - File doesn't appear to be a valid PKG file - 文件似乎不是有效的 PKG 文件 - Run Game 运行游戏 @@ -1498,14 +1399,6 @@ Eboot.bin file not found 找不到 Eboot.bin 文件 - - PKG File (*.PKG *.pkg) - PKG 文件(*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG是一个补丁或 DLC,请先安装游戏! - Game is already running! 游戏已经在运行中! @@ -1555,73 +1448,6 @@ 显示图标下的标签 - - PKGViewer - - Open Folder - 打开文件夹 - - - PKG ERROR - PKG 错误 - - - Name - 名称 - - - Serial - 序列号 - - - Installed - 已安装 - - - Size - 大小 - - - Category - 分类 - - - Type - 类型 - - - App Ver - 版本 - - - FW - 固件 - - - Region - 区域 - - - Flags - 标志 - - - Path - 路径 - - - File - 文件 - - - Unknown - 未知 - - - Package - Package - - SettingsDialog diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index f195ec1b7..08aa812fb 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -882,10 +882,6 @@ Error creating shortcut! Error creating shortcut! - - Install PKG - Install PKG - Game Game @@ -978,25 +974,6 @@ Keybindings - - InstallDirSelect - - shadPS4 - Choose directory - shadPS4 - Choose directory - - - Select which directory you want to install to. - Select which directory you want to install to. - - - Install All Queued to Selected Folder - Install All Queued to Selected Folder - - - Delete PKG File on Install - Delete PKG File on Install - - KBMSettings @@ -1214,10 +1191,6 @@ Open/Add Elf Folder Open/Add Elf Folder - - Install Packages (PKG) - Install Packages (PKG) - Boot Game Boot Game @@ -1234,10 +1207,6 @@ Configure... Configure... - - Install application from a .pkg file - Install application from a .pkg file - Recent Games Recent Games @@ -1314,10 +1283,6 @@ No games found. Please add your games to your library first. No games found. Please add your games to your library first. - - PKG Viewer - PKG Viewer - Search... Search... @@ -1426,70 +1391,6 @@ Only one file can be selected! 只能選擇一個檔案! - - PKG Extraction - PKG 解壓縮 - - - Patch detected! - 檢測到補丁! - - - PKG and Game versions match: - PKG 和遊戲版本匹配: - - - Would you like to overwrite? - 您想要覆蓋嗎? - - - PKG Version %1 is older than installed version: - PKG 版本 %1 比已安裝版本更舊: - - - Game is installed: - 遊戲已安裝: - - - Would you like to install Patch: - 您想要安裝補丁嗎: - - - DLC Installation - DLC 安裝 - - - Would you like to install DLC: %1? - 您想要安裝 DLC: %1 嗎? - - - DLC already installed: - DLC 已經安裝: - - - Game already installed - 遊戲已經安裝 - - - PKG ERROR - PKG 錯誤 - - - Extracting PKG %1/%2 - 正在解壓縮 PKG %1/%2 - - - Extraction Finished - 解壓縮完成 - - - Game successfully installed at %1 - 遊戲成功安裝於 %1 - - - File doesn't appear to be a valid PKG file - 檔案似乎不是有效的 PKG 檔案 - Run Game Run Game @@ -1498,14 +1399,6 @@ Eboot.bin file not found Eboot.bin file not found - - PKG File (*.PKG *.pkg) - PKG File (*.PKG *.pkg) - - - PKG is a patch or DLC, please install the game first! - PKG is a patch or DLC, please install the game first! - Game is already running! Game is already running! @@ -1555,73 +1448,6 @@ Show Labels Under Icons - - PKGViewer - - Open Folder - Open Folder - - - PKG ERROR - PKG 錯誤 - - - Name - 名稱 - - - Serial - 序號 - - - Installed - Installed - - - Size - 大小 - - - Category - Category - - - Type - Type - - - App Ver - App Ver - - - FW - FW - - - Region - 區域 - - - Flags - Flags - - - Path - 路徑 - - - File - File - - - Unknown - 未知 - - - Package - Package - - SettingsDialog From 8122f8ecbfc8d8a26ef616530befd381057ec59e Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sat, 29 Mar 2025 11:30:59 +0100 Subject: [PATCH 42/52] Diplay PR actions as `pr-{number}-{branchname}` (#2713) --- CMakeLists.txt | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd458f04e..804d5528f 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,7 @@ git_branch_name(GIT_BRANCH) string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S") message("start git things") + # Try to get the upstream remote and branch message("check for remote and branch") execute_process( @@ -123,6 +124,7 @@ execute_process( ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) + # If there's no upstream set or the command failed, check remote.pushDefault if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") message("check default push") @@ -134,15 +136,38 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() + # If running in GitHub Actions and the above fails if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") message("check github") set(GIT_REMOTE_NAME "origin") - if (DEFINED ENV{GITHUB_HEAD_REF}) # PR branch name - set(GIT_BRANCH "pr-$ENV{GITHUB_HEAD_REF}") - elseif (DEFINED ENV{GITHUB_REF}) # Normal branch name - string(REGEX REPLACE "^refs/[^/]*/" "" GIT_BRANCH "$ENV{GITHUB_REF}") + # Retrieve environment variables + if (DEFINED ENV{GITHUB_HEAD_REF}) + set(GITHUB_HEAD_REF "$ENV{GITHUB_HEAD_REF}") + else() + set(GITHUB_HEAD_REF "") + endif() + + if (DEFINED ENV{GITHUB_REF}) + string(REGEX REPLACE "^refs/[^/]*/" "" GITHUB_BRANCH "$ENV{GITHUB_REF}") + else() + set(GITHUB_BRANCH "") + endif() + + if (DEFINED ENV{GITHUB_EVENT_PATH}) + file(READ "$ENV{GITHUB_EVENT_PATH}" GITHUB_EVENT_JSON) + string(JSON PR_NUMBER GET ${GITHUB_EVENT_JSON} "number") + else() + set(PR_NUMBER "") + endif() + + if (NOT "${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_HEAD_REF}" STREQUAL "") + set(GIT_BRANCH "pr-${PR_NUMBER}-${GITHUB_HEAD_REF}") + elseif (NOT "${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_BRANCH}" STREQUAL "") + set(GIT_BRANCH "pr-${PR_NUMBER}-${GITHUB_BRANCH}") + elseif (NOT "${PR_NUMBER}" STREQUAL "") + set(GIT_BRANCH "pr-${PR_NUMBER}") else() message("couldn't find branch") set(GIT_BRANCH "detached-head") From 064c3161c3e0a4ddc2cbdf70fe4d307ad9d188c1 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Sat, 29 Mar 2025 15:58:10 +0000 Subject: [PATCH 43/52] real fix this time ? (#2717) --- src/qt_gui/gui_context_menus.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h index c13388bbc..b5732d0ca 100644 --- a/src/qt_gui/gui_context_menus.h +++ b/src/qt_gui/gui_context_menus.h @@ -140,12 +140,12 @@ public: QString open_update_path; Common::FS::PathToQString(open_update_path, m_games[itemID].path); open_update_path += "-UPDATE"; - if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { + if (std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); } else { Common::FS::PathToQString(open_update_path, m_games[itemID].path); open_update_path += "-patch"; - if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { + if (std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) { QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path)); } else { QMessageBox::critical(nullptr, tr("Error"), From faae1218fa0b590e4e3f55b7d41780eec8c281f9 Mon Sep 17 00:00:00 2001 From: Missake212 Date: Sat, 29 Mar 2025 22:50:22 +0000 Subject: [PATCH 44/52] Getting rid of the "Separate Update Folder" option (#2719) * real fix this time ? * should be all of it * Update en_US.ts --- src/common/config.cpp | 12 ------------ src/common/config.h | 2 -- src/qt_gui/settings_dialog.cpp | 6 ------ src/qt_gui/settings_dialog.ui | 7 ------- src/qt_gui/translations/en_US.ts | 8 -------- 5 files changed, 35 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index d1bb89897..2657cd12a 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -75,7 +75,6 @@ static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; static bool overrideControllerColor = false; static int controllerCustomColorRGB[3] = {0, 0, 255}; -static bool separateupdatefolder = false; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; static std::string trophyKey; @@ -352,10 +351,6 @@ void setVkGuestMarkersEnabled(bool enable) { vkGuestMarkers = enable; } -bool getSeparateUpdateEnabled() { - return separateupdatefolder; -} - bool getCompatibilityEnabled() { return compatibilityData; } @@ -517,10 +512,6 @@ void setIsMotionControlsEnabled(bool use) { isMotionControlsEnabled = use; } -void setSeparateUpdateEnabled(bool use) { - separateupdatefolder = use; -} - void setCompatibilityEnabled(bool use) { compatibilityData = use; } @@ -781,7 +772,6 @@ void load(const std::filesystem::path& path) { isAutoUpdate = toml::find_or(general, "autoUpdate", false); isAlwaysShowChangelog = toml::find_or(general, "alwaysShowChangelog", false); isSideTrophy = toml::find_or(general, "sideTrophy", "right"); - separateupdatefolder = toml::find_or(general, "separateUpdateEnabled", false); compatibilityData = toml::find_or(general, "compatibilityEnabled", false); checkCompatibilityOnStartup = toml::find_or(general, "checkCompatibilityOnStartup", false); @@ -977,7 +967,6 @@ void save(const std::filesystem::path& path) { data["General"]["autoUpdate"] = isAutoUpdate; data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog; data["General"]["sideTrophy"] = isSideTrophy; - data["General"]["separateUpdateEnabled"] = separateupdatefolder; data["General"]["compatibilityEnabled"] = compatibilityData; data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup; data["Input"]["cursorState"] = cursorState; @@ -1150,7 +1139,6 @@ void setDefaultValues() { emulator_language = "en_US"; m_language = 1; gpuId = -1; - separateupdatefolder = false; compatibilityData = false; checkCompatibilityOnStartup = false; backgroundImageOpacity = 50; diff --git a/src/common/config.h b/src/common/config.h index d040aa337..aba23621c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -35,7 +35,6 @@ bool getPlayBGM(); int getBGMvolume(); bool getisTrophyPopupDisabled(); bool getEnableDiscordRPC(); -bool getSeparateUpdateEnabled(); bool getCompatibilityEnabled(); bool getCheckCompatibilityOnStartup(); int getBackgroundImageOpacity(); @@ -105,7 +104,6 @@ void setNeoMode(bool enable); void setUserName(const std::string& type); void setUpdateChannel(const std::string& type); void setChooseHomeTab(const std::string& type); -void setSeparateUpdateEnabled(bool use); void setGameInstallDirs(const std::vector& dirs_config); void setAllGameInstallDirs(const std::vector& dirs_config); void setSaveDataPath(const std::filesystem::path& path); diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index 383cad8fa..25c27fef3 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -318,7 +318,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, // General ui->consoleLanguageGroupBox->installEventFilter(this); ui->emulatorLanguageGroupBox->installEventFilter(this); - ui->separateUpdatesCheckBox->installEventFilter(this); ui->showSplashCheckBox->installEventFilter(this); ui->discordRPCCheckbox->installEventFilter(this); ui->userName->installEventFilter(this); @@ -450,8 +449,6 @@ void SettingsDialog::LoadValuesFromConfig() { QString translatedText_FullscreenMode = screenModeMap.key(QString::fromStdString(Config::getFullscreenMode())); ui->displayModeComboBox->setCurrentText(translatedText_FullscreenMode); - ui->separateUpdatesCheckBox->setChecked( - toml::find_or(data, "General", "separateUpdateEnabled", false)); ui->gameSizeCheckBox->setChecked(toml::find_or(data, "GUI", "loadGameSizeEnabled", true)); ui->showSplashCheckBox->setChecked(toml::find_or(data, "General", "showSplash", false)); QString translatedText_logType = logTypeMap.key(QString::fromStdString(Config::getLogType())); @@ -600,8 +597,6 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("Console Language:\\nSets the language that the PS4 game uses.\\nIt's recommended to set this to a language the game supports, which will vary by region."); } else if (elementName == "emulatorLanguageGroupBox") { text = tr("Emulator Language:\\nSets the language of the emulator's user interface."); - } else if (elementName == "separateUpdatesCheckBox") { - text = tr("Enable Separate Update Folder:\\nEnables installing game updates into a separate folder for easy management.\\nThis can be manually created by adding the extracted update to the game folder with the name \"CUSA00000-UPDATE\" where the CUSA ID matches the game's ID."); } else if (elementName == "showSplashCheckBox") { text = tr("Show Splash Screen:\\nShows the game's splash screen (a special image) while the game is starting."); } else if (elementName == "discordRPCCheckbox") { @@ -760,7 +755,6 @@ void SettingsDialog::UpdateSettings() { Config::setVblankDiv(ui->vblankSpinBox->value()); Config::setDumpShaders(ui->dumpShadersCheckBox->isChecked()); Config::setNullGpu(ui->nullGpuCheckBox->isChecked()); - Config::setSeparateUpdateEnabled(ui->separateUpdatesCheckBox->isChecked()); Config::setLoadGameSizeEnabled(ui->gameSizeCheckBox->isChecked()); Config::setShowSplash(ui->showSplashCheckBox->isChecked()); Config::setDebugDump(ui->debugDump->isChecked()); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index 5600a0db7..20e26775d 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -135,13 +135,6 @@ 10 - - - - Enable Separate Update Folder - - - diff --git a/src/qt_gui/translations/en_US.ts b/src/qt_gui/translations/en_US.ts index 28d31b200..780f089e8 100644 --- a/src/qt_gui/translations/en_US.ts +++ b/src/qt_gui/translations/en_US.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings Default tab when opening settings @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulator Language:\nSets the language of the emulator's user interface. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. From bad82e72046b137d6592d7ebfb769d4ec3b1e257 Mon Sep 17 00:00:00 2001 From: DanielSvoboda Date: Sat, 29 Mar 2025 19:50:41 -0300 Subject: [PATCH 45/52] Fix Translation on Button in MainWindow (#2720) --- src/qt_gui/main_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 5d8f8e717..072ad70e5 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -45,11 +45,11 @@ MainWindow::~MainWindow() { bool MainWindow::Init() { auto start = std::chrono::steady_clock::now(); // setup ui + LoadTranslation(); AddUiWidgets(); CreateActions(); CreateRecentGameActions(); ConfigureGuiFromSettings(); - LoadTranslation(); CreateDockWindows(); CreateConnects(); SetLastUsedTheme(); From 99b90cbd5c5b8e24b0aa45630a4f8658267e8a6f Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sat, 29 Mar 2025 18:27:33 -0500 Subject: [PATCH 46/52] Minor libkernel changes (#2721) * sceKernelDebugOutText Some homebrew use this for logging, and these logs do show up in console klogs. I wasn't sure where the most suitable place for this function would be, so I made a separate file for these debug functions. * Implement kernel exit Some homebrew I have use this exit when an error occurs. Since actually closing the emulator isn't implemented yet, I've used an unreachable message that logs the status code. I've placed it in process.cpp for now, let me know if I should change that. * Improved implementations for sceKernelDebugRaiseException functions These functions take in two parameters, an error code and some other value that I have no idea what is for. If that second parameter is not zero, they return ORBIS_KERNEL_ERROR_EINVAL before any calls to mdbg_service. These improved implementations add the early error return and a message with the error code to the unreachable. * Add missing exports Homebrew apps like to use these kernel exports of posix functions instead. * Clang --- CMakeLists.txt | 2 ++ src/core/libraries/kernel/debug.cpp | 20 +++++++++++++++++++ src/core/libraries/kernel/debug.h | 14 +++++++++++++ src/core/libraries/kernel/kernel.cpp | 2 ++ src/core/libraries/kernel/process.cpp | 6 ++++++ .../libraries/kernel/threads/exception.cpp | 17 +++++++++++----- .../libraries/kernel/threads/pthread_spec.cpp | 5 +++++ 7 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/core/libraries/kernel/debug.cpp create mode 100644 src/core/libraries/kernel/debug.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 804d5528f..73a0e2c4c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,6 +324,8 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp src/core/libraries/kernel/threads/thread_state.h src/core/libraries/kernel/process.cpp src/core/libraries/kernel/process.h + src/core/libraries/kernel/debug.cpp + src/core/libraries/kernel/debug.h src/core/libraries/kernel/equeue.cpp src/core/libraries/kernel/equeue.h src/core/libraries/kernel/file_system.cpp diff --git a/src/core/libraries/kernel/debug.cpp b/src/core/libraries/kernel/debug.cpp new file mode 100644 index 000000000..0de67b8b5 --- /dev/null +++ b/src/core/libraries/kernel/debug.cpp @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "core/libraries/kernel/file_system.h" +#include "core/libraries/kernel/orbis_error.h" +#include "core/libraries/libs.h" + +namespace Libraries::Kernel { + +void PS4_SYSV_ABI sceKernelDebugOutText(void* unk, char* text) { + sceKernelWrite(1, text, strlen(text)); + return; +} + +void RegisterDebug(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("9JYNqN6jAKI", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugOutText); +} + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/debug.h b/src/core/libraries/kernel/debug.h new file mode 100644 index 000000000..177046862 --- /dev/null +++ b/src/core/libraries/kernel/debug.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Kernel { + +void RegisterDebug(Core::Loader::SymbolsResolver* sym); + +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 9227cf45a..33602bfe8 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -12,6 +12,7 @@ #include "common/va_ctx.h" #include "core/file_sys/fs.h" #include "core/libraries/error_codes.h" +#include "core/libraries/kernel/debug.h" #include "core/libraries/kernel/equeue.h" #include "core/libraries/kernel/file_system.h" #include "core/libraries/kernel/kernel.h" @@ -219,6 +220,7 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) { Libraries::Kernel::RegisterProcess(sym); Libraries::Kernel::RegisterException(sym); Libraries::Kernel::RegisterAio(sym); + Libraries::Kernel::RegisterDebug(sym); LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", 1, 1, kernel_ioctl); diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index d61ee37ac..02f8a538d 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -127,6 +127,11 @@ int PS4_SYSV_ABI sceKernelGetModuleInfoFromAddr(VAddr addr, int flags, return ORBIS_OK; } +s32 PS4_SYSV_ABI exit(s32 status) { + UNREACHABLE_MSG("Exiting with status code {}", status); + return 0; +} + void RegisterProcess(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion); LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, sceKernelIsNeoMode); @@ -136,6 +141,7 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym); LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind); LIB_FUNCTION("f7KBOafysXo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoFromAddr); + LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", 1, 1, exit); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index 5e2f35d69..e257cbea0 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/assert.h" +#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/threads/exception.h" #include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" @@ -148,13 +149,19 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { return 0; } -int PS4_SYSV_ABI sceKernelDebugRaiseException() { - UNREACHABLE(); +s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) { + if (unk != 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + UNREACHABLE_MSG("error {:#x}", error); return 0; } -int PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode() { - UNREACHABLE(); +s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(s32 error, s64 unk) { + if (unk != 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + UNREACHABLE_MSG("error {:#x}", error); return 0; } @@ -163,7 +170,7 @@ void RegisterException(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("WkwEd3N7w0Y", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelInstallExceptionHandler); LIB_FUNCTION("Qhv5ARAoOEc", "libkernel_unity", 1, "libkernel", 1, 1, - sceKernelRemoveExceptionHandler) + sceKernelRemoveExceptionHandler); LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseException); LIB_FUNCTION("zE-wXIZjLoM", "libkernel", 1, "libkernel", 1, 1, sceKernelDebugRaiseExceptionOnReleaseMode); diff --git a/src/core/libraries/kernel/threads/pthread_spec.cpp b/src/core/libraries/kernel/threads/pthread_spec.cpp index 9e625da32..b36e302d4 100644 --- a/src/core/libraries/kernel/threads/pthread_spec.cpp +++ b/src/core/libraries/kernel/threads/pthread_spec.cpp @@ -147,6 +147,11 @@ void RegisterSpec(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific); LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific); + // Posix-Kernel + LIB_FUNCTION("mqULNdimTn0", "libkernel", 1, "libkernel", 1, 1, posix_pthread_key_create); + LIB_FUNCTION("0-KXaS70xy4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_getspecific); + LIB_FUNCTION("WrOLvHU0yQM", "libkernel", 1, "libkernel", 1, 1, posix_pthread_setspecific); + // Orbis LIB_FUNCTION("geDaqgH9lTg", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_create)); LIB_FUNCTION("PrdHuuDekhY", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_key_delete)); From 3f33d218b35fcf7af7ca445c80a1f711eb77e474 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 30 Mar 2025 01:28:02 +0200 Subject: [PATCH 47/52] New Crowdin updates (#2715) * New translations en_us.ts (Italian) * New translations en_us.ts (Ukrainian) * New translations en_us.ts (Ukrainian) --- src/qt_gui/translations/it_IT.ts | 20 ++++++++++---------- src/qt_gui/translations/uk_UA.ts | 26 +++++++++++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index af321bd92..895908193 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -1409,43 +1409,43 @@ Play - Play + Riproduci Pause - Pause + Pausa Stop - Stop + Arresta Restart - Restart + Riavvia Full Screen - Full Screen + Schermo Intero Controllers - Controllers + Controller Keyboard - Keyboard + Tastiera Refresh List - Refresh List + Aggiorna Lista Resume - Resume + Riprendi Show Labels Under Icons - Show Labels Under Icons + Mostra Etichette Sotto Icone diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 890fa163e..5e2572449 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -1277,11 +1277,11 @@ Trophy Viewer - Trophy Viewer + Переглядач трофеїв No games found. Please add your games to your library first. - No games found. Please add your games to your library first. + Не знайдено жодної гри. Будь ласка, спочатку додайте свої ігри до бібліотеки. Search... @@ -1409,43 +1409,43 @@ Play - Play + Грати Pause - Pause + Пауза Stop - Stop + Стоп Restart - Restart + Перезапуск Full Screen - Full Screen + На повний екран Controllers - Controllers + Контролери Keyboard - Keyboard + Клавіатура Refresh List - Refresh List + Оновити список Resume - Resume + Продовжити Show Labels Under Icons - Show Labels Under Icons + Показати найменування під іконками @@ -2067,7 +2067,7 @@ Select Game: - Select Game: + Виберіть гру: Progress From f1e0a096d54ca2db48da67fb5210b3542fc1b858 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Sun, 30 Mar 2025 14:37:00 +0200 Subject: [PATCH 48/52] casually testing in production (#2718) --- CMakeLists.txt | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73a0e2c4c..021167d58 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,22 +143,26 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") set(GIT_REMOTE_NAME "origin") # Retrieve environment variables - if (DEFINED ENV{GITHUB_HEAD_REF}) + if (DEFINED ENV{GITHUB_HEAD_REF} AND NOT "$ENV{GITHUB_HEAD_REF}" STREQUAL "") + message("github head ref: $ENV{GITHUB_HEAD_REF}") set(GITHUB_HEAD_REF "$ENV{GITHUB_HEAD_REF}") else() set(GITHUB_HEAD_REF "") endif() - if (DEFINED ENV{GITHUB_REF}) + if (DEFINED ENV{GITHUB_REF} AND NOT "$ENV{GITHUB_REF}" STREQUAL "") + message("github ref: $ENV{GITHUB_REF}") string(REGEX REPLACE "^refs/[^/]*/" "" GITHUB_BRANCH "$ENV{GITHUB_REF}") + string(REGEX MATCH "refs/pull/([0-9]+)/merge" MATCHED_REF "$ENV{GITHUB_REF}") + if (MATCHED_REF) + set(PR_NUMBER "${CMAKE_MATCH_1}") + set(GITHUB_BRANCH "") + message("PR number: ${PR_NUMBER}") + else() + set(PR_NUMBER "") + endif() else() set(GITHUB_BRANCH "") - endif() - - if (DEFINED ENV{GITHUB_EVENT_PATH}) - file(READ "$ENV{GITHUB_EVENT_PATH}" GITHUB_EVENT_JSON) - string(JSON PR_NUMBER GET ${GITHUB_EVENT_JSON} "number") - else() set(PR_NUMBER "") endif() @@ -168,6 +172,12 @@ if (GIT_REMOTE_RESULT OR GIT_REMOTE_NAME STREQUAL "") set(GIT_BRANCH "pr-${PR_NUMBER}-${GITHUB_BRANCH}") elseif (NOT "${PR_NUMBER}" STREQUAL "") set(GIT_BRANCH "pr-${PR_NUMBER}") + elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_HEAD_REF}" STREQUAL "") + set(GIT_BRANCH "${GITHUB_HEAD_REF}") + elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_BRANCH}" STREQUAL "") + set(GIT_BRANCH "${GITHUB_BRANCH}") + elseif ("${PR_NUMBER}" STREQUAL "" AND NOT "${GITHUB_REF}" STREQUAL "") + set(GIT_BRANCH "${GITHUB_REF}") else() message("couldn't find branch") set(GIT_BRANCH "detached-head") From a707d31a4c23fdd8aefb146ae04d25ecbca246d0 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 30 Mar 2025 15:37:16 +0300 Subject: [PATCH 49/52] New Crowdin updates (#2722) * New translations en_us.ts (Albanian) * New translations en_us.ts (Swedish) * New translations en_us.ts (Polish) * New translations en_us.ts (Portuguese, Brazilian) * New translations en_us.ts (Spanish) * New translations en_us.ts (Romanian) * New translations en_us.ts (French) * New translations en_us.ts (Arabic) * New translations en_us.ts (Danish) * New translations en_us.ts (German) * New translations en_us.ts (Greek) * New translations en_us.ts (Finnish) * New translations en_us.ts (Hungarian) * New translations en_us.ts (Italian) * New translations en_us.ts (Japanese) * New translations en_us.ts (Korean) * New translations en_us.ts (Lithuanian) * New translations en_us.ts (Dutch) * New translations en_us.ts (Portuguese) * New translations en_us.ts (Russian) * New translations en_us.ts (Turkish) * New translations en_us.ts (Ukrainian) * New translations en_us.ts (Chinese Simplified) * New translations en_us.ts (Chinese Traditional) * New translations en_us.ts (Vietnamese) * New translations en_us.ts (Indonesian) * New translations en_us.ts (Persian) * New translations en_us.ts (Norwegian Bokmal) * New translations en_us.ts (Arabic) --- src/qt_gui/translations/ar_SA.ts | 56 ++++++++++++++------------------ src/qt_gui/translations/da_DK.ts | 8 ----- src/qt_gui/translations/de_DE.ts | 8 ----- src/qt_gui/translations/el_GR.ts | 8 ----- src/qt_gui/translations/es_ES.ts | 8 ----- src/qt_gui/translations/fa_IR.ts | 8 ----- src/qt_gui/translations/fi_FI.ts | 8 ----- src/qt_gui/translations/fr_FR.ts | 8 ----- src/qt_gui/translations/hu_HU.ts | 8 ----- src/qt_gui/translations/id_ID.ts | 8 ----- src/qt_gui/translations/it_IT.ts | 8 ----- src/qt_gui/translations/ja_JP.ts | 8 ----- src/qt_gui/translations/ko_KR.ts | 8 ----- src/qt_gui/translations/lt_LT.ts | 8 ----- src/qt_gui/translations/nb_NO.ts | 8 ----- src/qt_gui/translations/nl_NL.ts | 8 ----- src/qt_gui/translations/pl_PL.ts | 8 ----- src/qt_gui/translations/pt_BR.ts | 8 ----- src/qt_gui/translations/pt_PT.ts | 8 ----- src/qt_gui/translations/ro_RO.ts | 8 ----- src/qt_gui/translations/ru_RU.ts | 8 ----- src/qt_gui/translations/sq_AL.ts | 8 ----- src/qt_gui/translations/sv_SE.ts | 8 ----- src/qt_gui/translations/tr_TR.ts | 8 ----- src/qt_gui/translations/uk_UA.ts | 8 ----- src/qt_gui/translations/vi_VN.ts | 8 ----- src/qt_gui/translations/zh_CN.ts | 8 ----- src/qt_gui/translations/zh_TW.ts | 8 ----- 28 files changed, 24 insertions(+), 248 deletions(-) diff --git a/src/qt_gui/translations/ar_SA.ts b/src/qt_gui/translations/ar_SA.ts index f5503e189..c130a374c 100644 --- a/src/qt_gui/translations/ar_SA.ts +++ b/src/qt_gui/translations/ar_SA.ts @@ -1126,7 +1126,7 @@ Deadzone Offset (def 0.50): - Deadzone Offset (def 0.50): + : Speed Multiplier (def 1.0): @@ -1138,7 +1138,7 @@ This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. - This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config. + هذا الزر يقوم بنسخ تعيينات الأزرار من إعدادات المستخدم العامة لإعدادات المستخدم المحددة حالياً، ولا يمكن استعماله عندما تكون الإعدادات المستخدمة هي الإعدادات العامة. Copy values from Common Config @@ -1146,7 +1146,7 @@ Do you want to overwrite existing mappings with the mappings from the Common Config? - Do you want to overwrite existing mappings with the mappings from the Common Config? + هل تريد استبدال التعيينات الحالية بالتعيينات العامة؟ Unable to Save @@ -1474,10 +1474,6 @@ Emulator المحاكي - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings علامة التبويب الافتراضية عند فتح الإعدادات @@ -1500,7 +1496,7 @@ Trophy Key - Trophy Key + زر الميداليات Trophy @@ -1508,7 +1504,7 @@ Open the custom trophy images/sounds folder - Open the custom trophy images/sounds folder + افتح مجلد تخصيص اصوات/صور الميداليات Logger @@ -1544,7 +1540,7 @@ s - s + س Controller @@ -1588,7 +1584,7 @@ Enable HDR - Enable HDR + تشغيل HDR Paths @@ -1628,23 +1624,23 @@ Enable Crash Diagnostics - Enable Crash Diagnostics + تشغيل تشخيص الأعطال Collect Shaders - Collect Shaders + اجمع برامج التظليل Copy GPU Buffers - Copy GPU Buffers + انسخ التخزين المؤقت لوحدة معالجة الرُسوم Host Debug Markers - Host Debug Markers + استضافة علامات التصحيح Guest Debug Markers - Guest Debug Markers + ضيف علامات التصحيح Update @@ -1656,7 +1652,7 @@ Always Show Changelog - Always Show Changelog + اظهر سجل التغيرات دائماً Update Channel @@ -1672,23 +1668,23 @@ Title Music - Title Music + موسيقى الشاشة الرئيسية Disable Trophy Notification - Disable Trophy Notification + إغلاق إشعارات الميداليات Background Image - Background Image + صورة الخلفية Show Background Image - Show Background Image + إظهار صورة الخلفية Opacity - Opacity + درجة السواد Play title music @@ -1696,15 +1692,15 @@ Update Compatibility Database On Startup - Update Compatibility Database On Startup + تحديث قاعدة بيانات التوافق عند التشغيل Game Compatibility - Game Compatibility + توافق الألعاب Display Compatibility Data - Display Compatibility Data + إظهار معلومات التوافق Update Compatibility Database @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. لغة المحاكي:\nتحدد لغة واجهة المستخدم الخاصة بالمحاكي. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. إظهار شاشة البداية:\nيعرض شاشة البداية الخاصة باللعبة (صورة خاصة) أثناء بدء التشغيل. @@ -1760,7 +1752,7 @@ Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. - Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters. + مفتاح الميداليات:\nمفتاح يستخدم لفتح تشفير الميداليات. يجب أن يكون من جهاز مكسور الحماية.\nيجي أن يحتوي على أحرف نظام العد السداسي. Log Type:\nSets whether to synchronize the output of the log window for performance. May have adverse effects on emulation. @@ -1776,7 +1768,7 @@ Background Image:\nControl the opacity of the game background image. - Background Image:\nControl the opacity of the game background image. + صورة الخلفية:\nيتحكم في درجة سواد صورة خلفية اللعبة. Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. @@ -1784,7 +1776,7 @@ Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). - Disable Trophy Pop-ups:\nDisable in-game trophy notifications. Trophy progress can still be tracked using the Trophy Viewer (right-click the game in the main window). + إغلاق نوافذ الميداليات المنبثقة:\n إغلاق إشعارات الميداليات داخل اللعبة. تقدم الميداليات يمكن تتبعه باستخدام عارض الميداليات (قم بالضغط على زر الفأرة الأيمن داخل النافذة الرئيسية). Hide Cursor:\nChoose when the cursor will disappear:\nNever: You will always see the mouse.\nidle: Set a time for it to disappear after being idle.\nAlways: you will never see the mouse. diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 658ac118f..131a989e1 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings Standardfaneblad ved åbning af indstillinger @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulatorsprog:\nIndstiller sproget i emulatorens brugergrænseflade. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Vis startskærm:\nViser en startskærm (speciel grafik) under opstarten. diff --git a/src/qt_gui/translations/de_DE.ts b/src/qt_gui/translations/de_DE.ts index b6cd3105f..9642cd500 100644 --- a/src/qt_gui/translations/de_DE.ts +++ b/src/qt_gui/translations/de_DE.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Separaten Update-Ordner aktivieren - Default tab when opening settings Standardregisterkarte beim Öffnen der Einstellungen @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulatorsprache:\nLegt die Sprache der Emulator-Benutzeroberfläche fest. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Separaten Update-Ordner aktivieren:\nErmöglicht die Installation von Spielaktualiserungen in einem separaten Ordner zur einfachen Verwaltung. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Startbildschirm anzeigen:\nZeigt beim Start einen speziellen Bildschirm (Splash) des Spiels an. diff --git a/src/qt_gui/translations/el_GR.ts b/src/qt_gui/translations/el_GR.ts index d1cf0d4a6..c91e0c731 100644 --- a/src/qt_gui/translations/el_GR.ts +++ b/src/qt_gui/translations/el_GR.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings Προεπιλεγμένη καρτέλα κατά την ανοίγμα των ρυθμίσεων @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Γλώσσα Εξομοιωτή:\nΡυθμίζει τη γλώσσα του γραφικού περιβάλλοντος του εξομοιωτή. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Εμφάνιση Splash Screen:\nΕμφανίζει ειδική γραφική οθόνη κατά την εκκίνηση. diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index bbd49f61d..035aac6a3 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1474,10 +1474,6 @@ Emulator Emulador - - Enable Separate Update Folder - Habilitar Carpeta Independiente de Actualizaciones - Default tab when opening settings Pestaña predeterminada al abrir la configuración @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Idioma del Emulador:\nConfigura el idioma de la interfaz de usuario del emulador. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Habilitar Carpeta Independiente de Actualizaciónes:\nHabilita el instalar actualizaciones del juego en una carpeta separada para mas facilidad en la gestión.\nPuede crearse manualmente añadiendo la actualización extraída a la carpeta del juego con el nombre "CUSA00000-UPDATE" donde el CUSA ID coincide con el ID del juego. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Mostrar Pantalla de Inicio:\nMuestra la pantalla de inicio del juego (una imagen especial) mientras el juego se está iniciando. diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index f1f2c62ab..552a0ff23 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1474,10 +1474,6 @@ Emulator شبیه ساز - - Enable Separate Update Folder - فعال‌سازی پوشه جداگانه برای به‌روزرسانی - Default tab when opening settings زبان پیش‌فرض هنگام باز کردن تنظیمات @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. زبان شبیه‌ساز:\nزبان رابط کاربری شبیه‌ساز را انتخاب می‌کند. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - فعال‌سازی پوشه جداگانه برای به‌روزرسانی:\nامکان نصب به‌روزرسانی‌های بازی در یک پوشه جداگانه برای مدیریت راحت‌تر را فراهم می‌کند. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. نمایش صفحه شروع:\nصفحه شروع بازی (تصویری ویژه) را هنگام بارگذاری بازی نمایش می‌دهد. diff --git a/src/qt_gui/translations/fi_FI.ts b/src/qt_gui/translations/fi_FI.ts index d07f82bd5..44c668560 100644 --- a/src/qt_gui/translations/fi_FI.ts +++ b/src/qt_gui/translations/fi_FI.ts @@ -1474,10 +1474,6 @@ Emulator Emulaattori - - Enable Separate Update Folder - Ota Käyttöön Erillinen Päivityshakemisto - Default tab when opening settings Oletusvälilehti avattaessa asetuksia @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulaattorin Kieli:\nAsettaa emulaattorin käyttöliittymän kielen. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Ota Käyttöön Erillinen Päivityskansio:\nOttaa käyttöön päivitysten asennuksen erilliseen kansioon helpottamaan niiden hallintaa.\nTämä on tehtävissä manuaalisesti lisäämällä puretun päivityksen pelikansioon "CUSA00000-UPDATE" nimellä, missä CUSA ID vastaa pelin ID:tä. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Näytä Aloitusnäyttö:\nNäyttää pelin aloitusnäytön (erityinen kuva) pelin käynnistyessä. diff --git a/src/qt_gui/translations/fr_FR.ts b/src/qt_gui/translations/fr_FR.ts index 89599e32c..13e1be9f5 100644 --- a/src/qt_gui/translations/fr_FR.ts +++ b/src/qt_gui/translations/fr_FR.ts @@ -1474,10 +1474,6 @@ Emulator Émulateur - - Enable Separate Update Folder - Dossier séparé pour les mises à jour - Default tab when opening settings Onglet par défaut lors de l'ouverture des paramètres @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Langue de l'émulateur:\nDéfinit la langue de l'interface utilisateur de l'émulateur. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Dossier séparé pour les mises à jour:\nInstalle les mises à jours des jeux dans un dossier séparé pour une gestion plus facile. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Afficher l'écran de démarrage:\nAffiche l'écran de démarrage du jeu (une image spéciale) lors du démarrage du jeu. diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 85b6f2c95..58857d0d7 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1474,10 +1474,6 @@ Emulator Emulátor - - Enable Separate Update Folder - Külön Frissítési Mappa Engedélyezése - Default tab when opening settings Alapértelmezett fül a beállítások megnyitásakor @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulátor nyelve:\nBeállítja az emulátor felhasználói felületének nyelvét. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Külön Frissítéi Mappa Engedélyezése:\nEngedélyezi a frissítések külön mappába helyezését, a könnyű kezelésük érdekében. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Indítóképernyő megjelenítése:\nMegjeleníti a játék indítóképernyőjét (különleges képet) a játék elindításakor. diff --git a/src/qt_gui/translations/id_ID.ts b/src/qt_gui/translations/id_ID.ts index 1858da2a7..de19824f7 100644 --- a/src/qt_gui/translations/id_ID.ts +++ b/src/qt_gui/translations/id_ID.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings Tab default saat membuka pengaturan @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Bahasa Emulator:\nMenetapkan bahasa antarmuka pengguna emulator. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Tampilkan Layar Pembuka:\nMenampilkan layar pembuka permainan (gambar khusus) saat permainan dimulai. diff --git a/src/qt_gui/translations/it_IT.ts b/src/qt_gui/translations/it_IT.ts index 895908193..908013004 100644 --- a/src/qt_gui/translations/it_IT.ts +++ b/src/qt_gui/translations/it_IT.ts @@ -1474,10 +1474,6 @@ Emulator Emulatore - - Enable Separate Update Folder - Abilita Cartella Aggiornamenti Separata - Default tab when opening settings Scheda predefinita all'apertura delle impostazioni @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Lingua dell'Emulatore:\nImposta la lingua dell'interfaccia utente dell'emulatore. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Abilita Cartella Aggiornamenti Separata:\nAbilita l'installazione degli aggiornamenti in una cartella separata per una più facile gestione. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Mostra Schermata di Avvio:\nMostra la schermata di avvio del gioco (un'immagine speciale) mentre il gioco si sta avviando. diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index d4b8fa5b7..146caa515 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1474,10 +1474,6 @@ Emulator エミュレーター - - Enable Separate Update Folder - アップデートフォルダの分離を有効化 - Default tab when opening settings 設定を開くときのデフォルトタブ @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. エミュレーターの言語:\nエミュレーターのユーザーインターフェースの言語を設定します。 - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nゲームのアップデートを別のフォルダにインストールすることで、管理が容易になります。 - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. スプラッシュスクリーンを表示:\nゲーム起動中にゲームのスプラッシュスクリーン(特別な画像)を表示します。 diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index 9d4b58c79..b79959d38 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings 설정 열기 시 기본 탭 @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulator Language:\nSets the language of the emulator's user interface. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index 55a9fac0c..03ff5a003 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings Numatytoji kortelė atidarius nustatymus @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emuliatoriaus kalba:\nNustato emuliatoriaus vartotojo sąsajos kalbą. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Rodyti paleidimo ekraną:\nPaleidimo metu rodo žaidimo paleidimo ekraną (ypatingą vaizdą). diff --git a/src/qt_gui/translations/nb_NO.ts b/src/qt_gui/translations/nb_NO.ts index 4a0835c1c..e937287fd 100644 --- a/src/qt_gui/translations/nb_NO.ts +++ b/src/qt_gui/translations/nb_NO.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Bruk separat oppdateringsmappe - Default tab when opening settings Standardfanen når innstillingene åpnes @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulatorspråket:\nAngir språket for emulatorens brukergrensesnitt. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Bruk separat oppdateringsmappe:\n Gjør det mulig å installere spilloppdateringer i en egen mappe for enkel administrasjon.\nDette kan gjøres manuelt ved å legge til den utpakkede oppdateringen, til spillmappa med navnet "CUSA00000-UPDATE" der CUSA-ID-en samsvarer med spillets-ID. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Vis velkomstbilde:\nViser spillets velkomstbilde (et spesialbilde) når spillet starter. diff --git a/src/qt_gui/translations/nl_NL.ts b/src/qt_gui/translations/nl_NL.ts index 918da6a67..66872455e 100644 --- a/src/qt_gui/translations/nl_NL.ts +++ b/src/qt_gui/translations/nl_NL.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings Standaardtabblad bij het openen van instellingen @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulator Taal:\nStelt de taal van de gebruikersinterface van de emulator in. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Opstartscherm weergeven:\nToont het opstartscherm van het spel (een speciale afbeelding) tijdens het starten van het spel. diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index 39950d6ec..bd59a1894 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Włącz oddzielny folder aktualizacji - Default tab when opening settings Domyślna zakładka podczas otwierania ustawień @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Język emulatora:\nUstala język interfejsu użytkownika emulatora. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Włącz oddzielny folder aktualizacji:\nUmożliwia instalowanie aktualizacji gier w oddzielnym folderze w celu łatwego zarządzania. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Wyświetl ekran powitalny:\nPodczas uruchamiania gry wyświetla ekran powitalny (specjalny obraz). diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index 5d1582b5a..584d6dc19 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -1474,10 +1474,6 @@ Emulator Emulador - - Enable Separate Update Folder - Ativar Pasta de Atualização Separada - Default tab when opening settings Aba padrão ao abrir as configurações @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Idioma do Emulador:\nDefine o idioma da interface do emulador. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Ativar Pasta de Atualização Separada:\nPermite instalar atualizações de jogos em uma pasta separada para fácil gerenciamento.\nIsso pode ser manualmente criado adicionando a atualização extraída à pasta do jogo com o nome "CUSA00000-UPDATE" onde o ID do CUSA corresponde ao ID do jogo. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Mostrar Splash Inicial:\nExibe a tela inicial do jogo (imagem especial) ao iniciar o jogo. diff --git a/src/qt_gui/translations/pt_PT.ts b/src/qt_gui/translations/pt_PT.ts index a155a6324..70a73afe7 100644 --- a/src/qt_gui/translations/pt_PT.ts +++ b/src/qt_gui/translations/pt_PT.ts @@ -1474,10 +1474,6 @@ Emulator Emulador - - Enable Separate Update Folder - Ativar Pasta de Atualizações Separada - Default tab when opening settings Aba padrão ao abrir as definições @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Idioma do Emulador:\nDefine o idioma da interface gráfica do emulador. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Ativar Pasta de Atualização Separada:\nPermite instalar as atualizações dos jogos numa pasta separada para uma fácil gestão.\nIsto pode ser manualmente criado adicionando a atualização extraída à pasta do jogo com o nome "CUSA00000-UPDATE" onde o ID do CUSA corresponde ao ID do jogo. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Mostrar Splash Inicial:\nExibe o ecrã inicial do jogo (uma imagem especial) enquanto o jogo inicia. diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index 18121a204..78dd79c53 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings Tab-ul implicit la deschiderea setărilor @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Limba emulatorului:\nSetează limba interfeței utilizatorului a emulatorului. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Afișează ecranul de încărcare:\nAfișează ecranul de încărcare al jocului (o imagine specială) în timp ce jocul pornește. diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index 3944af589..0f16efc2c 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1474,10 +1474,6 @@ Emulator Эмулятор - - Enable Separate Update Folder - Отдельная папка обновлений - Default tab when opening settings Вкладка по умолчанию при открытии настроек @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Язык эмулятора:\nУстанавливает язык пользовательского интерфейса эмулятора. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Отдельная папка обновлений:\nПозволяет устанавливать обновления игры в отдельную папку для удобства.\nМожно создать вручную, добавив извлеченное обновление в папку с игрой с именем "CUSA00000-UPDATE", где идентификатор CUSA совпадает с идентификатором игры. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Показывать заставку:\nОтображает заставку игры (специальное изображение) во время запуска. diff --git a/src/qt_gui/translations/sq_AL.ts b/src/qt_gui/translations/sq_AL.ts index d59fd8c3e..311633a99 100644 --- a/src/qt_gui/translations/sq_AL.ts +++ b/src/qt_gui/translations/sq_AL.ts @@ -1474,10 +1474,6 @@ Emulator Emulatori - - Enable Separate Update Folder - Aktivizo dosjen e ndarë të përditësimit - Default tab when opening settings Skeda e paracaktuar kur hapen cilësimet @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Gjuha e emulatorit:\nPërcakton gjuhën e ndërfaqes së përdoruesit të emulatorit. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Aktivizo dosjen e ndarë të përditësimit:\nAktivizon instalimin e përditësimeve të lojërave në dosje të veçanta për menaxhim më të lehtë.\nKjo mund të krijohet manualisht duke shtuar përditësimin e shpaketuar në dosjen e lojës me emrin "CUSA00000-UPDATE" ku ID-ja CUSA përputhet me ID-në e lojës. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Shfaq ekranin e ngarkesës:\nShfaq ekranin e ngarkesës së lojës (një pamje e veçantë) gjatë fillimit të lojës. diff --git a/src/qt_gui/translations/sv_SE.ts b/src/qt_gui/translations/sv_SE.ts index b570c420e..ce0da785c 100644 --- a/src/qt_gui/translations/sv_SE.ts +++ b/src/qt_gui/translations/sv_SE.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Aktivera separat uppdateringsmapp - Default tab when opening settings Standardflik när inställningar öppnas @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emulatorspråk:\nStäller in språket för emulatorns användargränssnitt - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Aktivera separat uppdateringsmapp:\nAktiverar installation av speluppdateringar i en separat mapp för enkel hantering.\nDetta kan skapas manuellt genom att lägga till uppackad uppdatering till spelmappen med namnet "CUSA00000-UPDATE" där CUSA ID matchar spelets id - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Visa startskärm:\nVisar spelets startskärm (en speciell bild) när spelet startas diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index d2d018116..f5f7b65e5 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1474,10 +1474,6 @@ Emulator Emülatör - - Enable Separate Update Folder - Ayrı Güncelleme Klasörünü Etkinleştir - Default tab when opening settings Ayarlar açıldığında varsayılan sekme @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Emülatör Dili:\nEmülatörün kullanıcı arayüzünün dilini ayarlar. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Açılış Ekranını Göster:\nOyun açılırken (özel bir görüntü) açılış ekranını gösterir. diff --git a/src/qt_gui/translations/uk_UA.ts b/src/qt_gui/translations/uk_UA.ts index 5e2572449..3eb88bcab 100644 --- a/src/qt_gui/translations/uk_UA.ts +++ b/src/qt_gui/translations/uk_UA.ts @@ -1474,10 +1474,6 @@ Emulator Емулятор - - Enable Separate Update Folder - Увімкнути окрему папку оновлень - Default tab when opening settings Вкладка за замовчуванням при відкритті налаштувань @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Мова емулятора:\nВстановіть мову користувацького інтерфейсу емулятора. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Окрема папка для оновлень:\nДає змогу встановлювати оновлення гри в окрему папку для зручності. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Показувати заставку:\nВідображає заставку гри (спеціальне зображення) під час запуску гри. diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index b8c1759be..e0bc7e4fe 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1474,10 +1474,6 @@ Emulator Trình giả lập - - Enable Separate Update Folder - Bật thư mục cập nhật riêng - Default tab when opening settings Tab mặc định khi mở cài đặt @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. Ngôn ngữ của trình giả lập:\nChọn ngôn ngữ của giao diện người dùng của trình giả lập. - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. Hiển thị màn hình khởi động:\nHiển thị màn hình khởi động của trò chơi (một hình ảnh đặc biệt) trong khi trò chơi khởi động. diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index 7d414a493..120310810 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1474,10 +1474,6 @@ Emulator 模拟器 - - Enable Separate Update Folder - 启用单独的更新目录 - Default tab when opening settings 打开设置时的默认选项卡 @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. 模拟器语言:\n设置模拟器用户界面的语言。 - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - 启用单独的更新目录:\n启用安装游戏更新到一个单独的目录中以更便于管理。 - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. 显示启动画面:\n在游戏启动时显示游戏的启动画面(特殊图像)。 diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 08aa812fb..077455558 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1474,10 +1474,6 @@ Emulator Emulator - - Enable Separate Update Folder - Enable Separate Update Folder - Default tab when opening settings 打開設置時的默認選項卡 @@ -1742,10 +1738,6 @@ Emulator Language:\nSets the language of the emulator's user interface. 模擬器語言:\n設定模擬器的用戶介面的語言。 - - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.\nThis can be manually created by adding the extracted update to the game folder with the name "CUSA00000-UPDATE" where the CUSA ID matches the game's ID. - Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management. - Show Splash Screen:\nShows the game's splash screen (a special image) while the game is starting. 顯示啟動畫面:\n在遊戲啟動時顯示遊戲的啟動畫面(特殊圖片)。 From f85d8df71e770741768bb9c6e60123e2fe3019f7 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Sun, 30 Mar 2025 12:02:33 -0700 Subject: [PATCH 50/52] vulkan: Lower list primitive restart warning to debug log. (#2725) --- src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 901096259..c528258fb 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -77,8 +77,8 @@ GraphicsPipeline::GraphicsPipeline( auto prim_restart = key.enable_primitive_restart != 0; if (prim_restart && IsPrimitiveListTopology() && !instance.IsListRestartSupported()) { - LOG_WARNING(Render_Vulkan, - "Primitive restart is enabled for list topology but not supported by driver."); + LOG_DEBUG(Render_Vulkan, + "Primitive restart is enabled for list topology but not supported by driver."); prim_restart = false; } const vk::PipelineInputAssemblyStateCreateInfo input_assembly = { From 374b66ad8eae5c5d8a452f10973a321038aea451 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:27:12 -0500 Subject: [PATCH 51/52] Emulate sceKernelInternalMemory mapping (#2726) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Emulate sceKernelInternalMemory mapping This fixes the early crash in CUSA07820 (The Last of Us™ Part II). * Fix name --- src/core/linker.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 18ae62f4b..4ccb9d943 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -101,6 +101,17 @@ void Linker::Execute(const std::vector args) { memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2); + // Simulate sceKernelInternalMemory mapping, a mapping usually performed during libkernel init. + // Due to the large size of this mapping, failing to emulate it causes issues in some titles. + // This mapping belongs in the system reserved area, which starts at address 0x880000000. + static constexpr VAddr KernelAllocBase = 0x880000000ULL; + static constexpr s64 InternalMemorySize = 0x1000000; + void* addr_out{reinterpret_cast(KernelAllocBase)}; + + const s32 ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory( + &addr_out, InternalMemorySize, 3, 0, "SceKernelInternalMemory"); + ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping"); + main_thread.Run([this, module, args](std::stop_token) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); @@ -372,7 +383,8 @@ void* Linker::AllocateTlsForThread(bool is_primary) { // If sceKernelMapNamedFlexibleMemory is being called from libkernel and addr = 0 // it automatically places mappings in system reserved area instead of managed. - static constexpr VAddr KernelAllocBase = 0x880000000ULL; + // Since the system reserved area already has a mapping in it, this address is slightly higher. + static constexpr VAddr KernelAllocBase = 0x881000000ULL; // The kernel module has a few different paths for TLS allocation. // For SDK < 1.7 it allocates both main and secondary thread blocks using libc mspace/malloc. From b0a12c02e16c02379385f675dd4c6318c0b7fd69 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Mon, 31 Mar 2025 04:55:14 -0500 Subject: [PATCH 52/52] Add S_SETPRIO to EmitFlowControl (#2727) By squidbus' suggestion, I've added a warning log for this case. --- src/shader_recompiler/frontend/translate/scalar_flow.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/scalar_flow.cpp b/src/shader_recompiler/frontend/translate/scalar_flow.cpp index ef8bab789..0e02b77a2 100644 --- a/src/shader_recompiler/frontend/translate/scalar_flow.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_flow.cpp @@ -13,6 +13,9 @@ void Translator::EmitFlowControl(u32 pc, const GcnInst& inst) { case Opcode::S_TTRACEDATA: LOG_WARNING(Render_Vulkan, "S_TTRACEDATA instruction!"); return; + case Opcode::S_SETPRIO: + LOG_WARNING(Render_Vulkan, "S_SETPRIO instruction!"); + return; case Opcode::S_GETPC_B64: return S_GETPC_B64(pc, inst); case Opcode::S_WAITCNT:

aHH*iAmjzIlNYQ}fyp$U6# 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