Compare commits

..

11 Commits

Author SHA1 Message Date
DraVee 58f101f51d ... 2026-05-10 04:10:04 +02:00
DraVee ea3aaa1d8a [file_sys] Check for Mods the same way on SDMC and non-SDMC
* fix getting cheats from SDMC (allow usage with aio-switch-updater)
2026-05-10 04:10:04 +02:00
DraVee 50c53a7614 chore: minor changes 2026-05-10 04:10:04 +02:00
DraVee 38bc172acf [dmnt] Fix CheatCompatibility
* now it follows Atmosphere system to only enable cheat if cheat_name.txt = build_id
* also deduplicate active update

Signed-off-by: DraVee <chimera@dravee.dev>
2026-05-10 04:10:04 +02:00
xbzk 27036f376a [fixup] fix problem introduced on fix merge conflicts
Signed-off-by: xbzk <xbzk@eden-emu.dev>
2026-05-10 04:10:04 +02:00
Maufeat 206254035b [cheat] add dmnt, indiviual cheats, etc.
* DraVee: fixed rebase conflicts
* xbzk: fixed Android after upstream changes
2026-05-10 04:10:04 +02:00
John 732fee2e85 Add Enable Legacy Rescale Pass to Android (#3851)
Testing: Luigi Mansion 3 artifact lines also happen on android.

Toggle existed on every platform but android. It works on Android and also removes the artifact lines that also happen on intel and amd gpus.

A testing PR until #3665

Co-authored-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3851
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-09 23:49:14 +02:00
lizzie 86f2f0bc36 [*] Re-fix clang-cl building (#3940)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3940
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-09 16:26:17 +02:00
Eden CI cad9db4886 [dist, android] Update translations from Transifex for May 09 (#3941)
Automatic translation update for May 09

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3941
2026-05-09 16:22:25 +02:00
lizzie 672c21829b [core/hle/kernel] Remove redundant TLS load/stores, reuse computed segment+address instead (#3932)
While originally for MSVC, this also should help clang/gcc not die trying to make codegen for the load/store of fields for the tls_data

should help to reuse computed values instead of recomputing shit for no reason

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3932
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-09 05:48:20 +02:00
lizzie eec460ec2e [dynarmic] remove decode matcher function handlers using std::function<>, use raw function pointers (#3920)
issues:
- std::function<> is used, which is famously bad
- storage of tehse in tables makes big fucking tables for no good reason
- lets just store a normal pointer and stuff! :)

this pr attempts to address that

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3920
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-09 05:41:41 +02:00
82 changed files with 2885 additions and 1549 deletions
+25 -19
View File
@@ -169,6 +169,31 @@ if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /W3 /WX-")
endif()
# Set runtime library to MD/MDd for all configurations
if(MSVC)
if (YUZU_USE_BUNDLED_QT AND ARCHITECTURE_arm64)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set(libflag MT)
else()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
set(libflag MD)
endif()
# Force all projects (including external dependencies) to use the same runtime
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /${libflag}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /${libflag}d")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /${libflag}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /${libflag}d")
# Add this to ensure Cubeb uses the same runtime
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:Debug>:/${libflag}d>>
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:Release>:/${libflag}>>
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:RelWithDebInfo>:/${libflag}>>
$<$<COMPILE_LANGUAGE:C,CXX>:$<$<CONFIG:MinSizeRel>:/${libflag}>>
)
endif()
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Build SDL2 from external source" OFF "NOT MSVC;NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "NOT ANDROID" OFF)
@@ -688,25 +713,6 @@ if (MSVC AND CXX_CLANG)
link_libraries(llvm-mingw-runtime)
endif()
# Set runtime library to MD/MDd for all configurations
if(MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
# Force all projects (including external dependencies) to use the same runtime
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MD")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MDd")
# Add this to ensure Cubeb uses the same runtime
add_compile_options(
$<$<CONFIG:Debug>:/MDd>
$<$<CONFIG:Release>:/MD>
$<$<CONFIG:RelWithDebInfo>:/MD>
$<$<CONFIG:MinSizeRel>:/MD>
)
endif()
add_subdirectory(src)
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
+16 -10
View File
@@ -6038,47 +6038,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL غير متوفر!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>OpenGL لا يتم دعم السياقات المشتركة</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>OpenGL لم يتم تجميع إيدن بدعم</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>OpenGL حدث خطأ أثناء تهيئة</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>أو قد لا يكون لديك أحدث برنامج تشغيل للرسومات OpenGL قد لا تدعم بطاقة الرسومات الخاصة بك</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>OpenGL 4.6 حدث خطأ أثناء تهيئة</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>أو قد لا يكون لديك أحدث برنامج تشغيل للرسومات OpenGL 4.6 قد لا تدعم بطاقة الرسومات الخاصة بك.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>قد لا تدعم وحدة معالجة الرسومات لديك ملحقًا واحدًا أو أكثر من ملحقات OpenGL المطلوبة. يُرجى التأكد من تثبيت أحدث برنامج تشغيل للرسومات.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;1%&lt;br&gt;&lt;br&gt;إضافات غير مدعومة: &lt;br&gt;2%</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation>هذا الإصدار لا يدعم OpenGL.</translation>
</message>
</context>
<context>
<name>GameList</name>
+17 -11
View File
@@ -494,7 +494,7 @@ This is mainly a debug option and shouldn&apos;t be disabled.</source>
<message>
<location filename="../../src/qt_common/config/shared_translation.cpp" line="66"/>
<source>Memory Layout</source>
<translation type="unfinished"/>
<translation>Distribució de memòria</translation>
</message>
<message>
<location filename="../../src/qt_common/config/shared_translation.cpp" line="67"/>
@@ -5972,47 +5972,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL no disponible!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Error al inicialitzar OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>La seva GPU no suporta OpenGL, o no té instal·lat els últims controladors gràfics.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Error inicialitzant OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>La seva GPU no suporta OpenGL 4.6, o no té instal·lats els últims controladors gràfics.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>És possible que la seva GPU no suporti una o més extensions necessàries d&apos;OpenGL. Si us plau, asseguris de tenir els últims controladors de la tarjeta gràfica.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Extensions no suportades:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation>Aquesta compilació no té suport per a OpenGL.</translation>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5963,47 +5963,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL není k dispozici!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Chyba při inicializaci OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Vaše grafická karta pravděpodobně nepodporuje OpenGL nebo nejsou nainstalovány nejnovější ovladače.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Chyba při inicializaci OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Vaše grafická karta pravděpodobně nepodporuje OpenGL 4.6 nebo nejsou nainstalovány nejnovější ovladače.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Vaše grafická karta pravděpodobně nepodporuje jedno nebo více rozšíření OpenGL. Ujistěte se prosím, že jsou nainstalovány nejnovější ovladače.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Nepodporované rozšíření:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5971,47 +5971,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5986,47 +5986,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL nicht verfügbar!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Gemeinsame OpenGL-Kontexte werden nicht unterstützt.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Fehler beim Initialisieren von OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Deine Grafikkarte unterstützt kein OpenGL oder du hast nicht den neusten Treiber installiert.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Fehler beim Initialisieren von OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Deine Grafikkarte unterstützt OpenGL 4.6 nicht, oder du benutzt nicht die neuste Treiberversion.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Deine Grafikkarte unterstützt anscheinend nicht eine oder mehrere von yuzu benötigten OpenGL-Erweiterungen. Bitte stelle sicher, dass du den neusten Grafiktreiber installiert hast.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Nicht unterstützte Erweiterungen:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5962,47 +5962,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>Το OpenGL δεν είναι διαθέσιμο!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Σφάλμα κατα την αρχικοποίηση του OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6053,47 +6053,53 @@ Por favor, vaya a Configuración -&gt; Sistema -&gt; Red y selecciona una interf
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>¡OpenGL no está disponible!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Los contextos compartidos de OpenGL no son compatibles.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden no ha sido compilado con soporte para OpenGL.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>¡Error al inicializar OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Tu GPU no soporta OpenGL, o no tienes instalados los últimos controladores gráficos.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>¡Error al iniciar OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Tu GPU no soporta OpenGL 4.6, o no tienes instalado el último controlador de la tarjeta gráfica.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Es posible que la GPU no soporte una o más extensiones necesarias de OpenGL . Por favor, asegúrate de tener los últimos controladores de la tarjeta gráfica.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Extensiones no soportadas:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation>Esta compilación no tiene soporte para OpenGL.</translation>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5940,47 +5940,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>openGL ei ole saatavilla!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Virhe käynnistäessä OpenGL ydintä!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6029,47 +6029,53 @@ Veuillez aller dans Configurer -&gt; Système -&gt; Réseau puis en choisir une.
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL n&apos;est pas disponible !</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Les contextes OpenGL partagés ne sont pas pris en charge.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden n&apos;a pas été compilé avec le support OpenGL</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Erreur lors de l&apos;initialisation d&apos;OpenGL !</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Votre GPU peut ne pas prendre en charge OpenGL, ou vous n&apos;avez pas les derniers pilotes graphiques.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Erreur lors de l&apos;initialisation d&apos;OpenGL 4.6 !</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Votre GPU peut ne pas prendre en charge OpenGL 4.6 ou vous ne disposez pas du dernier pilote graphique: %1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Votre GPU peut ne pas prendre en charge une ou plusieurs extensions OpenGL requises. Veuillez vous assurer que vous disposez du dernier pilote graphique.&lt;br&gt;&lt;br&gt;GL Renderer :&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Extensions non prises en charge :&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5955,47 +5955,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL nem elérhető!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Hiba történt az OpenGL inicializálása során!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Lehetséges, hogy a GPU-d nem támogatja az OpenGL-t, vagy nem a legfrissebb grafikus illesztőprogram van telepítve.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Hiba történt az OpenGL 4.6 inicializálása során!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Lehetséges, hogy a GPU-d nem támogatja az OpenGL 4.6-ot, vagy nem a legfrissebb grafikus illesztőprogram van telepítve.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Előfordulhat, hogy a GPU-d nem támogat egy vagy több szükséges OpenGL kiterjesztést. Győződj meg róla, hogy a legújabb videokártya-illesztőprogramot használod.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Nem támogatott kiterjesztések:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5991,47 +5991,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL tidak tersedia!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Terjadi kesalahan menginisialisasi OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>VGA anda mungkin tidak mendukung OpenGL, atau anda tidak memiliki pemacu piranti (driver) grafis terbaharu.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Terjadi kesalahan menginisialisasi OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>VGA anda mungkin tidak mendukung OpenGL 4.6, atau anda tidak memiliki pemacu piranti (driver) grafis terbaharu.&lt;br&gt;&lt;br&gt;Pemuat GL:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>VGA anda mungkin tidak mendukung satu atau lebih ekstensi OpenGL. Mohon pastikan bahwa anda memiliki pemacu piranti (driver) grafis terbaharu.&lt;br&gt;&lt;br&gt;Pemuat GL:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Ekstensi yang tidak didukung:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6052,47 +6052,53 @@ Vai su Configura -&gt; Sistema -&gt; Rete e selezionane una.</translation>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL non disponibile!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Gli shared context di OpenGL non sono supportati.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden non è stato compilato con il supporto a OpenGL.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Errore durante l&apos;inizializzazione di OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>La tua GPU potrebbe non supportare OpenGL, o non hai installato l&apos;ultima versione dei driver video.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Errore durante l&apos;inizializzazione di OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>La tua GPU potrebbe non supportare OpenGL 4.6, o non hai installato l&apos;ultima versione dei driver video.&lt;br&gt;&lt;br&gt;Renderer GL:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>La tua GPU potrebbe non supportare una o più estensioni OpenGL richieste. Assicurati di aver installato i driver video più recenti.&lt;br&gt;&lt;br&gt;Renderer GL:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Estensioni non supportate:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5978,47 +5978,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGLは使用できません!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>OpenGL初期化エラー</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>GPUがOpenGLをサポートしていないか、グラフィックスドライバーが最新ではありません。</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>OpenGL4.6初期化エラー!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>GPUがOpenGL4.6をサポートしていないか、グラフィックスドライバーが最新ではありません。&lt;br&gt;&lt;br&gt;GL レンダラ:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>GPUが1つ以上の必要なOpenGL拡張機能をサポートしていない可能性があります。最新のグラフィックドライバを使用していることを確認してください。&lt;br&gt;&lt;br&gt;GL レンダラ:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;サポートされていない拡張機能:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5976,47 +5976,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL을 사용할 수 없습니다!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>OpenGL 공유 컨텍스트는 지원되지 않습니다.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>OpenGL을 초기화하는 동안 오류가 발생했습니다!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>사용하시는 GPU가 OpenGL을 지원하지 않거나, 최신 그래픽 드라이버가 설치되어 있지 않습니다.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>OpenGL 4.6 초기화 중 오류 발생!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>사용하시는 GPU가 OpenGL 4.6을 지원하지 않거나 최신 그래픽 드라이버가 설치되어 있지 않습니다. &lt;br&gt;&lt;br&gt;GL 렌더링 장치:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>사용하시는 GPU가 1개 이상의 OpenGL 확장 기능을 지원하지 않습니다. 최신 그래픽 드라이버가 설치되어 있는지 확인하세요. &lt;br&gt;&lt;br&gt;GL 렌더링 장치:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;지원하지 않는 확장 기능:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5975,47 +5975,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL ikke tilgjengelig!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Delte OpenGL-kontekster støttes ikke.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Feil under initialisering av OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Det kan hende at GPU-en din ikke støtter OpenGL, eller at du ikke har den nyeste grafikkdriveren.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Feil under initialisering av OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Det kan hende at GPU-en din ikke støtter OpenGL 4.6, eller at du ikke har den nyeste grafikkdriveren.&lt;br&gt;&lt;br&gt;GL-renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Det kan hende at GPU-en din ikke støtter én eller flere nødvendige OpenGL-utvidelser. Vennligst sørg for at du har den nyeste grafikkdriveren.&lt;br&gt;&lt;br&gt;GL-renderer: &lt;br&gt;%1&lt;br&gt;&lt;br&gt;Ikke-støttede utvidelser:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5973,47 +5973,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL niet beschikbaar!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>OpenGL gedeelde contexten worden niet ondersteund.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Fout tijdens het initialiseren van OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Je GPU ondersteunt mogelijk geen OpenGL, of je hebt niet de laatste grafische stuurprogramma.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Fout tijdens het initialiseren van OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Je GPU ondersteunt mogelijk OpenGL 4.6 niet, of je hebt niet het laatste grafische stuurprogramma.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Je GPU ondersteunt mogelijk een of meer vereiste OpenGL-extensies niet. Zorg ervoor dat je het laatste grafische stuurprogramma hebt.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Ondersteunde extensies:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6041,47 +6041,53 @@ Przejdź do sekcji Konfiguracja -&gt; System -&gt; Sieć i dokonaj wyboru.</tran
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL niedostępny!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Współdzielone konteksty OpenGL nie są obsługiwane.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden nie został skompilowany z obsługą OpenGL.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Błąd podczas inicjowania OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Twoja karta graficzna może nie obsługiwać OpenGL lub nie masz najnowszych sterowników karty graficznej.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Błąd podczas inicjowania OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Twoja karta graficzna może nie obsługiwać OpenGL 4.6 lub nie masz najnowszych sterowników karty graficznej.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Twoja karta graficzna może nie obsługiwać co najmniej jednego wymaganego rozszerzenia OpenGL. Upewnij się, że masz najnowsze sterowniki karty graficznej&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Nieobsługiwane rozszerzenia:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6028,47 +6028,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL não está disponível!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Shared contexts do OpenGL não são suportados.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Erro ao inicializar OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>O seu GPU pode não suportar OpenGL, ou não tem os drivers gráficos mais recentes.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Erro ao inicializar o OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>O teu GPU pode não suportar OpenGL 4.6, ou não tem os drivers gráficos mais recentes.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Sua GPU pode não suportar uma ou mais extensões necessárias do OpenGL. Verifique se você possui a última versão dos drivers gráficos.&lt;br&gt;&lt;br&gt;Renderizador GL:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Extensões não suportadas:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5976,47 +5976,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL não está disponível!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Shared contexts do OpenGL não são suportados.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Erro ao inicializar OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>O seu GPU pode não suportar OpenGL, ou não tem os drivers gráficos mais recentes.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Erro ao inicializar o OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>O teu GPU pode não suportar OpenGL 4.6, ou não tem os drivers gráficos mais recentes.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Sua GPU pode não suportar uma ou mais extensões necessárias do OpenGL. Verifique se você possui a última versão dos drivers gráficos.&lt;br&gt;&lt;br&gt;Renderizador GL:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Extensões não suportadas:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6056,47 +6056,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL не доступен!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Общие контексты OpenGL не поддерживаются.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden не был скомпилирован с поддержкой OpenGL.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Ошибка при инициализации OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Ваш ГП может не поддерживать OpenGL, или у вас установлен устаревший графический драйвер.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Ошибка при инициализации OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Ваш ГП может не поддерживать OpenGL 4.6, или у вас установлен устаревший графический драйвер.&lt;br&gt;&lt;br&gt;Рендерер GL:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Ваш ГП может не поддерживать одно или несколько требуемых расширений OpenGL. Пожалуйста, убедитесь в том, что у вас установлен последний графический драйвер.&lt;br&gt;&lt;br&gt;Рендерер GL:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Неподдерживаемые расширения:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation>В этой сборке отсутствует поддержка OpenGL.</translation>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6063,47 +6063,53 @@ Gå till Konfigurera -&gt; System -&gt; Nätverk och gör ett val.</translation>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL är inte tillgängligt!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Delade OpenGL-kontexter stöds inte.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden har inte kompilerats med OpenGL-stöd.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Fel vid initiering av OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Din GPU kanske inte stöder OpenGL, eller så har du inte den senaste grafikdrivrutinen.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Fel vid initiering av OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Din GPU kanske inte stöder OpenGL 4.6, eller så har du inte den senaste grafikdrivrutinen.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Din GPU kanske inte stöder ett eller flera av de nödvändiga OpenGL-tilläggen. Se till att du har den senaste grafikdrivrutinen.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Tillägg som inte stöds:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6004,47 +6004,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL kullanıma uygun değil!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>OpenGL paylaşılan bağlam desteklenmiyor.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>OpenGl başlatılırken bir hata oluştu!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>GPU&apos;nuz OpenGL desteklemiyor veya güncel bir grafik sürücüsüne sahip değilsiniz.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>OpenGl 4.6 başlatılırken bir hata oluştu!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>GPU&apos;nuz OpenGL 4.6&apos;yı desteklemiyor veya güncel bir grafik sürücüsüne sahip değilsiniz.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>GPU&apos;nuz gereken bir yada daha fazla OpenGL eklentisini desteklemiyor Lütfen güncel bir grafik sürücüsüne sahip olduğunuzdan emin olun.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt; Desteklenmeyen Eklentiler:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6058,47 +6058,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL недоступний!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Спільні контексти OpenGL не підтримуються.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden не скомпільовано з підтримкою OpenGL.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Помилка під час ініціалізації OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>Ваш ГП може не підтримувати OpenGL або у вас встановлено застарілий графічний драйвер.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Помилка під час ініціалізації OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>Ваш ГП може не підтримувати OpenGL 4.6 або у вас встановлено застарілий графічний драйвер.&lt;br&gt;&lt;br&gt;Візуалізатор GL:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>Ваш ГП може не підтримувати одне або кілька розширень, необхідних для OpenGL. Переконайтеся, що у вас встановлено останній графічний драйвер.&lt;br&gt;&lt;br&gt;Візуалізатор GL:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Непідтримувані розширення:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation>Ця збірка не підтримує OpenGL.</translation>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5976,47 +5976,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL không khả dụng!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Các ngữ cảnh OpenGL chung không được hỗ trợ.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Lỗi khi khởi tạo OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>GPU của bạn có thể không hỗ trợ OpenGL, hoặc bạn không có driver đồ hoạ mới nhất.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Lỗi khi khởi tạo OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>GPU của bạn có thể không hỗ trợ OpenGL 4.6, hoặc bạn không có driver đồ hoạ mới nhất.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>GPU của bạn có thể không hỗ trợ một hoặc nhiều tiện ích OpenGL cần thiết. Vui lòng đảm bảo bạn có driver đồ hoạ mới nhất.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Tiện ích không hỗ trợ:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -5976,47 +5976,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>Không có sẵn OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>Các ngữ cảnh OpenGL chung không được hỗ trợ.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>Đã xảy ra lỗi khi khởi tạo OpenGL!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>GPU của bạn có thể không hỗ trợ OpenGL, hoặc bạn không có driver đồ hoạ mới nhất.</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>Lỗi khi khởi tạo OpenGL 4.6!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>GPU của bạn có thể không hỗ trợ OpenGL 4.6, hoặc bạn không có driver đồ hoạ mới nhất.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>GPU của bạn có thể không hỗ trợ một hoặc nhiều tiện ích OpenGL cần thiết. Vui lòng đảm bảo bạn có driver đồ hoạ mới nhất.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Tiện ích không hỗ trợ:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6044,47 +6044,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>OpenGL 模式不可用!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>不支持 OpenGL 共享上下文。</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation>Eden 尚未编译为支持 OpenGL。</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>初始化 OpenGL 时出错!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>您的 GPU 可能不支持 OpenGL ,或者您没有安装最新的显卡驱动。</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>初始化 OpenGL 4.6 时出错!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>您的 GPU 可能不支持 OpenGL 4.6 ,或者您没有安装最新的显卡驱动。&lt;br&gt;&lt;br&gt;GL 渲染器:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>您的 GPU 可能不支持某些必需的 OpenGL 扩展。请确保您已经安装最新的显卡驱动。&lt;br&gt;&lt;br&gt;GL 渲染器:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;不支持的扩展:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+16 -10
View File
@@ -6003,47 +6003,53 @@ Please go to Configure -&gt; System -&gt; Network and make a selection.</source>
<context>
<name>GRenderWindow</name>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>OpenGL not available!</source>
<translation>無法使用 OpenGL 模式!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
<source>OpenGL shared contexts are not supported.</source>
<translation>不支援 OpenGL 共用的上下文。</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
<source>Eden has not been compiled with OpenGL support.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
<source>Error while initializing OpenGL!</source>
<translation>初始化 OpenGL 時發生錯誤!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
<translation>您的 GPU 可能不支援 OpenGL,或是未安裝最新的圖形驅動程式</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
<source>Error while initializing OpenGL 4.6!</source>
<translation>初始化 OpenGL 4.6 時發生錯誤!</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1</source>
<translation>您的 GPU 可能不支援 OpenGL 4.6,或是未安裝最新的圖形驅動程式&lt;br&gt;&lt;br&gt;GL 渲染器:&lt;br&gt;%1</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;GL Renderer:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;%2</source>
<translation>您的 GPU 可能不支援某些必需的 OpenGL 功能。請確保您已安裝最新的圖形驅動程式。&lt;br&gt;&lt;br&gt;GL 渲染器:&lt;br&gt;%1&lt;br&gt;&lt;br&gt;不支援的功能:&lt;br&gt;%2</translation>
</message>
<message>
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
<source>This build doesn&apos;t have OpenGL support.</source>
<translation type="unfinished"/>
</message>
</context>
<context>
<name>GameList</name>
+2 -2
View File
@@ -1259,7 +1259,7 @@ class ParameterPack final : public Node {
// Setup OutputBuffer for a pack expansion, unless we're already expanding
// one.
void initializePackExpansion(OutputBuffer &OB) const {
if (OB.CurrentPackMax == std::numeric_limits<unsigned>::max()) {
if (OB.CurrentPackMax == (std::numeric_limits<unsigned>::max)()) {
OB.CurrentPackMax = static_cast<unsigned>(Data.size());
OB.CurrentPackIndex = 0;
}
@@ -1353,7 +1353,7 @@ public:
const Node *getChild() const { return Child; }
void printLeft(OutputBuffer &OB) const override {
constexpr unsigned Max = std::numeric_limits<unsigned>::max();
constexpr unsigned Max = (std::numeric_limits<unsigned>::max)();
ScopedOverride<unsigned> SavePackIdx(OB.CurrentPackIndex, Max);
ScopedOverride<unsigned> SavePackMax(OB.CurrentPackMax, Max);
size_t StreamPos = OB.getCurrentPosition();
+2 -2
View File
@@ -88,8 +88,8 @@ public:
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
/// into the pack that we're currently printing.
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
unsigned CurrentPackIndex = (std::numeric_limits<unsigned>::max)();
unsigned CurrentPackMax = (std::numeric_limits<unsigned>::max)();
/// When zero, we're printing template args and '>' needs to be parenthesized.
/// Use a counter so we can simply increment inside parentheses.
+7 -4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 1996 Arthur David Olson
// SPDX-License-Identifier: BSD-2-Clause
@@ -466,8 +469,8 @@ CalendarTimeInternal* timesub(const time_t* timep, s64 offset, const Rule* sp,
int signed_y = static_cast<s32>(y);
tmp->tm_year = signed_y - TM_YEAR_BASE;
}
else if ((!std::is_signed_v<time_t> || std::numeric_limits<s32>::min() + TM_YEAR_BASE <= y) &&
y - TM_YEAR_BASE <= std::numeric_limits<s32>::max()) {
else if ((!std::is_signed_v<time_t> || (std::numeric_limits<s32>::min)() + TM_YEAR_BASE <= y) &&
y - TM_YEAR_BASE <= (std::numeric_limits<s32>::max)()) {
tmp->tm_year = static_cast<s32>(y - TM_YEAR_BASE);
}
else {
@@ -558,8 +561,8 @@ CalendarTimeInternal* localsub(Rule const* sp, time_t const* timep, s64 setname,
else {
newy += years;
}
if (!(std::numeric_limits<s32>::min() <= newy &&
newy <= std::numeric_limits<s32>::max())) {
if (!((std::numeric_limits<s32>::min)() <= newy &&
newy <= (std::numeric_limits<s32>::max)())) {
return nullptr;
}
result->tm_year = static_cast<s32>(newy);
+7 -4
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-FileCopyrightText: 1996 Arthur David Olson
// SPDX-License-Identifier: BSD-2-Clause
@@ -26,10 +29,10 @@ constexpr size_t TZ_MAX_CHARS = 50;
constexpr size_t MY_TZNAME_MAX = 255;
constexpr size_t TZNAME_MAXIMUM = 255;
constexpr size_t TZ_MAX_LEAPS = 50;
constexpr s64 TIME_T_MAX = std::numeric_limits<s64>::max();
constexpr s64 TIME_T_MIN = std::numeric_limits<s64>::min();
constexpr s64 TIME_T_MAX = (std::numeric_limits<s64>::max)();
constexpr s64 TIME_T_MIN = (std::numeric_limits<s64>::min)();
constexpr size_t CHARS_EXTRA = 3;
constexpr size_t MAX_ZONE_CHARS = std::max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof("UTC"));
constexpr size_t MAX_ZONE_CHARS = (std::max)(TZ_MAX_CHARS + CHARS_EXTRA, sizeof("UTC"));
constexpr size_t MAX_TZNAME_CHARS = 2 * (MY_TZNAME_MAX + 1);
struct ttinfo {
@@ -51,7 +54,7 @@ struct Rule {
std::array<s64, TZ_MAX_TIMES> ats;
std::array<u8, TZ_MAX_TIMES> types;
std::array<ttinfo, TZ_MAX_TYPES> ttis;
std::array<char, std::max(MAX_ZONE_CHARS, MAX_TZNAME_CHARS)> chars;
std::array<char, (std::max)(MAX_ZONE_CHARS, MAX_TZNAME_CHARS)> chars;
s32 defaulttype;
std::array <u8, 0x12C4> padding1;
};
+1 -1
View File
@@ -185,7 +185,7 @@ else()
add_compile_definitions(_FILE_OFFSET_BITS=64)
endif()
if (YUZU_STATIC_BUILD AND NOT APPLE)
if (YUZU_STATIC_BUILD AND NOT APPLE AND NOT MSVC)
add_compile_options(-static)
# yuzu-cmd requires us to explicitly link libpthread, libgcc, and libstdc++ as static
@@ -8,6 +8,8 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
import org.yuzu.yuzu_emu.model.Patch
import org.yuzu.yuzu_emu.model.PatchType
@@ -24,13 +26,25 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
inner class AddonViewHolder(val binding: ListItemAddonBinding) :
AbstractViewHolder<Patch>(binding) {
override fun bind(model: Patch) {
binding.addonCard.setOnClickListener {
binding.addonSwitch.performClick()
val isCheat = model.isCheat()
val isIncompatible = isCheat && model.cheatCompat == Patch.CHEAT_COMPAT_INCOMPATIBLE
val indentPx = if (isCheat) {
(32 * binding.root.context.resources.displayMetrics.density).toInt()
} else {
0
}
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
marginStart = indentPx
}
binding.addonCard.setOnClickListener(
if (isIncompatible) null else { { binding.addonSwitch.performClick() } }
)
binding.title.text = model.name
binding.version.text = model.version
binding.addonSwitch.setOnCheckedChangeListener(null)
binding.addonSwitch.isChecked = model.enabled
binding.addonSwitch.isEnabled = !isIncompatible
binding.addonSwitch.alpha = if (isIncompatible) 0.38f else 1.0f
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
if (PatchType.from(model.type) == PatchType.Update && checked) {
addonViewModel.enableOnlyThisUpdate(model)
@@ -39,12 +53,9 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
model.enabled = checked
}
}
val canDelete = model.isRemovable
binding.deleteCard.isEnabled = canDelete
binding.buttonDelete.isEnabled = canDelete
binding.deleteCard.alpha = if (canDelete) 1f else 0.38f
val canDelete = model.isRemovable && !isCheat
binding.deleteCard.isVisible = canDelete
binding.buttonDelete.isVisible = canDelete
if (canDelete) {
val deleteAction = {
addonViewModel.setAddonToDelete(model)
@@ -72,7 +72,7 @@ class AddonsFragment : Fragment() {
}
addonViewModel.addonList.collect(viewLifecycleOwner) {
(binding.listAddons.adapter as AddonAdapter).submitList(it)
(binding.listAddons.adapter as AddonAdapter).submitList(it.toList())
}
addonViewModel.showModInstallPicker.collect(
viewLifecycleOwner,
@@ -31,12 +31,17 @@ class AddonViewModel : ViewModel() {
val addonToDelete = _addonToDelete.asStateFlow()
var game: Game? = null
private set
private var loadedGameKey: String? = null
private val isRefreshing = AtomicBoolean(false)
private val pendingRefresh = AtomicBoolean(false)
fun onAddonsViewCreated(game: Game) {
if (this.game?.programId == game.programId && _patchList.value.isNotEmpty()) {
return
}
this.game = game
refreshAddons(commitEmpty = false)
}
@@ -66,8 +71,7 @@ class AddonViewModel : ViewModel() {
NativeLibrary.getPatchesForFile(currentGame.path, currentGame.programId)
} ?: return@launch
val patchList = patches.toMutableList()
patchList.sortBy { it.name }
val patchList = sortPatchesWithCheatsGrouped(patches.toMutableList())
// Ensure only one update is enabled
ensureSingleUpdateEnabled(patchList)
@@ -146,6 +150,7 @@ class AddonViewModel : ViewModel() {
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
PatchType.DLC -> NativeLibrary.removeDLC(patch.programId)
PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name)
PatchType.Cheat -> {}
}
refreshAddons(force = true)
}
@@ -179,7 +184,7 @@ class AddonViewModel : ViewModel() {
it.name
}
} else {
it.name
it.getStorageKey()
}
}
}.toTypedArray()
@@ -201,4 +206,28 @@ class AddonViewModel : ViewModel() {
private fun gameKey(game: Game): String {
return "${game.programId}|${game.path}"
}
private fun sortPatchesWithCheatsGrouped(patches: MutableList<Patch>): MutableList<Patch> {
val individualCheats = patches.filter { it.isCheat() }
val nonCheats = patches.filter { !it.isCheat() }.sortedBy { it.name }
val cheatsByParent = individualCheats.groupBy { it.parentName }
val result = mutableListOf<Patch>()
for (patch in nonCheats) {
result.add(patch)
cheatsByParent[patch.name]?.sortedBy { it.name }?.let { childCheats ->
result.addAll(childCheats)
}
}
val knownParents = nonCheats.map { it.name }.toSet()
for ((parentName, orphanCheats) in cheatsByParent) {
if (parentName !in knownParents) {
result.addAll(orphanCheats.sortedBy { it.name })
}
}
return result
}
}
@@ -17,7 +17,9 @@ data class Patch(
val programId: String,
val titleId: String,
val numericVersion: Long = 0,
val source: Int = 0
val source: Int = 0,
val parentName: String = "", // For cheats: name of the mod folder containing them
val cheatCompat: Int = CHEAT_COMPAT_COMPATIBLE
) {
companion object {
const val SOURCE_UNKNOWN = 0
@@ -25,8 +27,31 @@ data class Patch(
const val SOURCE_SDMC = 2
const val SOURCE_EXTERNAL = 3
const val SOURCE_PACKED = 4
const val CHEAT_COMPAT_COMPATIBLE = 0
const val CHEAT_COMPAT_INCOMPATIBLE = 1
}
val isRemovable: Boolean
get() = source != SOURCE_EXTERNAL && source != SOURCE_PACKED
/**
* Returns the storage key used for saving enabled/disabled state.
* For cheats with a parent, returns "ParentName::CheatName".
*/
fun getStorageKey(): String {
return if (parentName.isNotEmpty()) {
"$parentName::$name"
} else {
name
}
}
/**
* Returns true if this patch is an individual cheat entry (not a cheat mod).
* Individual cheats have type=Cheat and a parent mod name.
*/
fun isCheat(): Boolean = type == PatchType.Cheat.int && parentName.isNotEmpty()
fun isIncompatibleCheat(): Boolean = isCheat() && cheatCompat == CHEAT_COMPAT_INCOMPATIBLE
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -6,7 +9,8 @@ package org.yuzu.yuzu_emu.model
enum class PatchType(val int: Int) {
Update(0),
DLC(1),
Mod(2);
Mod(2),
Cheat(3);
companion object {
fun from(int: Int): PatchType = entries.firstOrNull { it.int == int } ?: Update
+3 -1
View File
@@ -1406,7 +1406,9 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
Common::Android::ToJString(env, std::to_string(patch.program_id)),
Common::Android::ToJString(env, std::to_string(patch.title_id)),
static_cast<jlong>(patch.numeric_version), static_cast<jint>(patch.source));
static_cast<jlong>(patch.numeric_version), static_cast<jint>(patch.source),
Common::Android::ToJString(env, patch.parent_name),
static_cast<jint>(patch.cheat_compat));
env->SetObjectArrayElement(jpatchArray, i, jpatch);
++i;
}
@@ -512,7 +512,7 @@
<string name="emulate_bgr565">Emulate BGR565</string>
<string name="emulate_bgr565_description">Fixes problems with inverted colors in games or strange artifacts or strange shadows.</string>
<string name="rescale_hack">Enable Legacy Rescale Pass</string>
<string name="rescale_hack_description">Fixes Luigi Mansion 3 artifact lines.</string>
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
+1 -1
View File
@@ -516,7 +516,7 @@ namespace Common::Android {
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID(
patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JILjava/lang/String;)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
+7 -2
View File
@@ -554,8 +554,13 @@ struct Values {
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
Category::RendererHacks};
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
Category::RendererHacks};
SwitchableSetting<bool> rescale_hack{linkage,
#ifdef __ANDROID__
true,
#else
false,
#endif
"rescale_hack", Category::RendererHacks};
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks};
+12 -5
View File
@@ -616,6 +616,18 @@ add_library(core STATIC
hle/service/caps/caps_u.h
hle/service/cmif_serialization.h
hle/service/cmif_types.h
hle/service/dmnt/cheat_interface.cpp
hle/service/dmnt/cheat_interface.h
hle/service/dmnt/cheat_parser.cpp
hle/service/dmnt/cheat_parser.h
hle/service/dmnt/cheat_process_manager.cpp
hle/service/dmnt/cheat_process_manager.h
hle/service/dmnt/cheat_virtual_machine.cpp
hle/service/dmnt/cheat_virtual_machine.h
hle/service/dmnt/dmnt.cpp
hle/service/dmnt/dmnt.h
hle/service/dmnt/dmnt_results.h
hle/service/dmnt/dmnt_types.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp
@@ -1153,11 +1165,6 @@ add_library(core STATIC
loader/xci.h
memory.cpp
memory.h
memory/cheat_engine.cpp
memory/cheat_engine.h
memory/dmnt_cheat_types.h
memory/dmnt_cheat_vm.cpp
memory/dmnt_cheat_vm.h
perf_stats.cpp
perf_stats.h
reporter.cpp
+3 -4
View File
@@ -761,8 +761,7 @@ bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
}
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
fpsimd_context* fpsimd_context) {
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context, fpsimd_context* fpsimd_context) {
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
u64& sp = *reinterpret_cast<u64*>(&context->sp);
@@ -772,9 +771,9 @@ std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, m
u32 instruction = memory.Read32(pc);
bool was_executed = false;
auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction);
auto decoder = Dynarmic::A64::Decode<VisitorBase, bool>(visitor, instruction);
if (decoder) {
was_executed = decoder->get().call(visitor, instruction);
was_executed = *decoder;
} else {
was_executed = false;
}
+137 -11
View File
@@ -40,6 +40,7 @@
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/process_creation.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/glue/time/static.h"
@@ -54,7 +55,6 @@
#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/tools/freezer.h"
@@ -278,8 +278,17 @@ struct System::Impl {
audio_core.emplace(system);
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
// Create cheat_manager BEFORE services, as DMNT::LoopProcess needs it
cheat_manager = std::make_unique<Service::DMNT::CheatProcessManager>(system);
services.emplace(service_manager, system, stop_event.get_token());
// Apply any pending cheats that were registered before cheat_manager was initialized
if (pending_cheats.has_pending) {
ApplyPendingCheats(system);
}
is_powered_on = true;
exit_locked = false;
exit_requested = false;
@@ -340,11 +349,6 @@ struct System::Impl {
return init_result;
}
// Initialize cheat engine
if (cheat_engine) {
cheat_engine->Initialize();
}
// Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
@@ -405,7 +409,6 @@ struct System::Impl {
services.reset();
service_manager.reset();
fs_controller.Reset();
cheat_engine.reset();
core_timing.ClearPendingEvents();
app_loader.reset();
audio_core.reset();
@@ -475,7 +478,6 @@ struct System::Impl {
std::optional<Tegra::Host1x::Host1x> host1x_core;
std::optional<Core::DeviceMemory> device_memory;
std::optional<AudioCore::AudioCore> audio_core;
std::optional<Memory::CheatEngine> cheat_engine;
std::optional<Tools::Freezer> memory_freezer;
std::optional<Tools::RenderdocAPI> renderdoc_api;
@@ -495,6 +497,17 @@ struct System::Impl {
std::unique_ptr<Tegra::GPU> gpu_core;
std::stop_source stop_event;
/// Cheat Manager (DMNT)
std::unique_ptr<Service::DMNT::CheatProcessManager> cheat_manager;
/// Pending cheats to register after cheat_manager is initialized
struct PendingCheats {
std::vector<Service::DMNT::CheatEntry> list;
std::array<u8, 32> build_id{};
u64 main_region_begin{};
u64 main_region_size{};
bool has_pending{false};
} pending_cheats;
mutable std::mutex suspend_guard;
std::mutex general_channel_mutex;
std::atomic_bool is_paused{};
@@ -512,6 +525,61 @@ struct System::Impl {
general_channel_event.emplace(*general_channel_context);
}
}
void ApplyPendingCheats(System& system) {
if (!pending_cheats.has_pending || !cheat_manager) {
return;
}
LOG_DEBUG(Core, "Applying {} pending cheats", pending_cheats.list.size());
const auto result = cheat_manager->AttachToApplicationProcess(
pending_cheats.build_id, pending_cheats.main_region_begin,
pending_cheats.main_region_size);
if (result.IsError()) {
LOG_WARNING(Core, "Failed to attach cheat process: result={}", result.raw);
pending_cheats = {};
return;
}
LOG_DEBUG(Core, "Cheat process attached successfully");
for (const auto& entry : pending_cheats.list) {
if (entry.cheat_id == 0 && entry.definition.num_opcodes != 0) {
LOG_DEBUG(Core, "Setting master cheat '{}' with {} opcodes",
entry.definition.readable_name.data(), entry.definition.num_opcodes);
const auto set_result = cheat_manager->SetMasterCheat(entry.definition);
if (set_result.IsError()) {
LOG_WARNING(Core, "Failed to set master cheat: result={}", set_result.raw);
}
break;
}
}
// Add normal cheats (cheat_id != 0)
for (const auto& entry : pending_cheats.list) {
if (entry.cheat_id == 0 || entry.definition.num_opcodes == 0) {
continue;
}
u32 assigned_id = 0;
LOG_DEBUG(Core, "Adding cheat '{}' (enabled={}, {} opcodes)",
entry.definition.readable_name.data(), entry.enabled,
entry.definition.num_opcodes);
const auto add_result = cheat_manager->AddCheat(assigned_id, entry.enabled,
entry.definition);
if (add_result.IsError()) {
LOG_WARNING(Core,
"Failed to add cheat (original_id={} enabled={} name='{}'): result={}",
entry.cheat_id, entry.enabled,
entry.definition.readable_name.data(), add_result.raw);
}
}
// Clear pending cheats
pending_cheats = {};
}
};
System::System() : impl{std::make_unique<Impl>(*this)} {}
@@ -749,11 +817,61 @@ FileSys::VirtualFilesystem System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
void System::RegisterCheatList(const std::vector<Service::DMNT::CheatEntry>& list,
const std::array<u8, 32>& build_id, u64 main_region_begin,
u64 main_region_size) {
impl->cheat_engine.emplace(*this, list, build_id);
impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
// If cheat_manager is not yet initialized, cache the cheats for later
if (!impl->cheat_manager) {
impl->pending_cheats.list = list;
impl->pending_cheats.build_id = build_id;
impl->pending_cheats.main_region_begin = main_region_begin;
impl->pending_cheats.main_region_size = main_region_size;
impl->pending_cheats.has_pending = true;
LOG_INFO(Core, "Cached {} cheats for later registration", list.size());
return;
}
// Attach cheat process to the current application process
const auto result = impl->cheat_manager->AttachToApplicationProcess(build_id, main_region_begin,
main_region_size);
if (result.IsError()) {
LOG_WARNING(Core, "Failed to attach cheat process: result={}", result.raw);
return;
}
// Empty list: nothing more to do
if (list.empty()) {
return;
}
// Set master cheat if present (cheat_id == 0)
for (const auto& entry : list) {
if (entry.cheat_id == 0 && entry.definition.num_opcodes != 0) {
const auto set_result = impl->cheat_manager->SetMasterCheat(entry.definition);
if (set_result.IsError()) {
LOG_WARNING(Core, "Failed to set master cheat: result={}", set_result.raw);
}
// Only one master cheat allowed
break;
}
}
// Add normal cheats (cheat_id != 0)
for (const auto& entry : list) {
if (entry.cheat_id == 0 || entry.definition.num_opcodes == 0) {
continue;
}
u32 assigned_id = 0;
const auto add_result = impl->cheat_manager->AddCheat(assigned_id, entry.enabled,
entry.definition);
if (add_result.IsError()) {
LOG_WARNING(Core,
"Failed to add cheat (original_id={} enabled={} name='{}'): result={}",
entry.cheat_id, entry.enabled,
entry.definition.readable_name.data(), add_result.raw);
}
}
}
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
@@ -893,6 +1011,14 @@ Tools::RenderdocAPI& System::GetRenderdocAPI() {
return *impl->renderdoc_api;
}
Service::DMNT::CheatProcessManager& System::GetCheatManager() {
return *impl->cheat_manager;
}
const Service::DMNT::CheatProcessManager& System::GetCheatManager() const {
return *impl->cheat_manager;
}
void System::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
return impl->kernel.RunServer(std::move(server_manager));
}
+10 -2
View File
@@ -17,6 +17,7 @@
#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/service/dmnt/dmnt_types.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/kernel_helpers.h"
@@ -45,10 +46,14 @@ enum class ResultStatus : u16;
} // namespace Loader
namespace Core::Memory {
struct CheatEntry;
class Memory;
} // namespace Core::Memory
namespace Service::DMNT {
class CheatProcessManager;
struct CheatEntry;
}
namespace Service {
namespace Account {
@@ -335,7 +340,7 @@ public:
[[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
void RegisterCheatList(const std::vector<Service::DMNT::CheatEntry>& list,
const std::array<u8, 0x20>& build_id, u64 main_region_begin,
u64 main_region_size);
@@ -376,6 +381,9 @@ public:
[[nodiscard]] Tools::RenderdocAPI& GetRenderdocAPI();
[[nodiscard]] Service::DMNT::CheatProcessManager& GetCheatManager();
[[nodiscard]] const Service::DMNT::CheatProcessManager& GetCheatManager() const;
void SetExitLocked(bool locked);
bool GetExitLocked() const;
+323 -283
View File
@@ -27,12 +27,13 @@
#include "core/file_sys/vfs/vfs_cached.h"
#include "core/file_sys/vfs/vfs_layered.h"
#include "core/file_sys/vfs/vfs_vector.h"
#include "core/hle/service/dmnt/cheat_parser.h"
#include "core/hle/service/dmnt/dmnt_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/set/settings_server.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
#include "core/memory/cheat_engine.h"
namespace FileSys {
namespace {
@@ -64,16 +65,15 @@ std::string FormatTitleVersion(u32 version,
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
// doesn't have a directory with name.
VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
// Returns a directory with name matching case-insensitively.
// Returns nullptr if directory doesn't contain a subdirectory with the given name.
VirtualDir FindSubdirectoryCaseless(const VirtualDir& dir, std::string_view name) {
#ifdef _WIN32
return dir->GetSubdirectory(name);
#else
const auto subdirs = dir->GetSubdirectories();
for (const auto& subdir : subdirs) {
std::string dir_name = Common::ToLower(subdir->GetName());
if (dir_name == name) {
const auto target = Common::ToLower(std::string(name));
for (const auto& subdir : dir->GetSubdirectories()) {
if (Common::ToLower(subdir->GetName()) == target) {
return subdir;
}
}
@@ -82,36 +82,36 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
#endif
}
std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
std::optional<std::vector<Service::DMNT::CheatEntry>> ReadCheatFileFromFolder(
u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto build_id = build_id_raw.substr(0, std::min(build_id_raw.size(), sizeof(u64) * 2));
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
if (file == nullptr) {
LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
title_id, build_id);
LOG_DEBUG(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
title_id, build_id);
return std::nullopt;
}
std::vector<u8> data(file->GetSize());
if (file->Read(data.data(), data.size()) != data.size()) {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
title_id, build_id);
LOG_WARNING(Common_Filesystem,
"Failed to read cheats file for title_id={:016X}, build_id={}", title_id,
build_id);
return std::nullopt;
}
const Core::Memory::TextCheatParser parser;
const Service::DMNT::CheatParser parser;
return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
}
void AppendCommaIfNotEmpty(std::string& to, std::string_view with) {
if (to.empty()) {
to += with;
} else {
if (!to.empty()) {
to += ", ";
to += with;
}
to += with;
}
bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
@@ -130,8 +130,7 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
}
auto control_nca = provider->GetEntry(update_tid, ContentRecordType::Control);
if (control_nca == nullptr ||
control_nca->GetStatus() != Loader::ResultStatus::Success) {
if (control_nca == nullptr || control_nca->GetStatus() != Loader::ResultStatus::Success) {
return {};
}
@@ -169,16 +168,11 @@ u64 PatchManager::GetTitleID() const {
return title_id;
}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr)
return exefs;
PatchManager::UpdateResolution PatchManager::GetActiveUpdate(ContentRecordType type) const {
UpdateResolution update_res{};
const auto& disabled = Settings::values.disabled_addons[title_id];
bool update_disabled = true;
std::optional<u32> enabled_version;
bool checked_external = false;
bool checked_manual = false;
@@ -195,8 +189,10 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
checked_external = true;
for (const auto& update_entry : update_versions) {
if (!IsVersionedExternalUpdateDisabled(disabled, update_entry.version)) {
update_disabled = false;
enabled_version = update_entry.version;
update_res.update_disabled = false;
update_res.update_active_version = update_entry.version;
update_res.update_active_raw = external_provider->GetEntryForVersion(
update_tid, type, update_entry.version);
break;
}
}
@@ -214,8 +210,10 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
checked_manual = true;
for (const auto& update_entry : manual_update_versions) {
if (!IsVersionedExternalUpdateDisabled(disabled, update_entry.version)) {
update_disabled = false;
enabled_version = update_entry.version;
update_res.update_disabled = false;
update_res.update_active_version = update_entry.version;
update_res.update_active_raw = manual_provider->GetEntryForVersion(
update_tid, type, update_entry.version);
break;
}
}
@@ -227,37 +225,41 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
// check for original NAND style
// Check NAND if: no external updates exist, OR all external updates are disabled
if (!checked_external && !checked_manual) {
// Only enable NAND update if it exists AND is not disabled
// We need to check if an update actually exists in the content provider
const bool has_nand_update = content_provider.HasEntry(update_tid, ContentRecordType::Program);
const bool nand_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update (NAND)") != disabled.cend();
const bool sdmc_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update (SDMC)") != disabled.cend();
const bool generic_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
if (has_nand_update) {
const bool nand_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (NAND)") != disabled.cend();
const bool sdmc_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (SDMC)") != disabled.cend();
const bool generic_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
if (!nand_disabled && !sdmc_disabled && !generic_disabled) {
update_disabled = false;
}
if (!nand_disabled && !sdmc_disabled && !generic_disabled) {
update_res.update_active_raw = content_provider.GetEntryRaw(update_tid, type);
update_res.update_disabled = update_res.update_active_raw == nullptr;
}
} else if (update_disabled && content_union) {
const bool nand_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (NAND)") != disabled.cend();
const bool sdmc_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (SDMC)") != disabled.cend();
} else if (update_res.update_disabled && content_union) {
const bool nand_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update (NAND)") != disabled.cend();
const bool sdmc_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update (SDMC)") != disabled.cend();
if (!nand_disabled || !sdmc_disabled) {
const auto nand_sdmc_entries = content_union->ListEntriesFilterOrigin(
std::nullopt, TitleType::Update, ContentRecordType::Program, update_tid);
std::nullopt, TitleType::Update, type, update_tid);
for (const auto& [slot, entry] : nand_sdmc_entries) {
if (slot == ContentProviderUnionSlot::UserNAND ||
slot == ContentProviderUnionSlot::SysNAND) {
if (!nand_disabled) {
update_disabled = false;
update_res.update_active_raw =
content_provider.GetEntryRaw(update_tid, type);
update_res.update_disabled = update_res.update_active_raw == nullptr;
break;
}
} else if (slot == ContentProviderUnionSlot::SDMC) {
if (!sdmc_disabled) {
update_disabled = false;
update_res.update_active_raw =
content_provider.GetEntryRaw(update_tid, type);
update_res.update_disabled = update_res.update_active_raw == nullptr;
break;
}
}
@@ -265,44 +267,36 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
}
// Game Updates
return update_res;
}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr)
return exefs;
const auto update_tid = GetUpdateTitleID(title_id);
const auto update_res = GetActiveUpdate();
std::unique_ptr<NCA> update = nullptr;
// If we have a specific enabled version from external provider, use it
if (enabled_version.has_value() && content_union) {
const auto* external_provider = content_union->GetExternalProvider();
if (external_provider) {
auto file = external_provider->GetEntryForVersion(update_tid, ContentRecordType::Program, *enabled_version);
if (file != nullptr) {
update = std::make_unique<NCA>(file);
}
}
// Also try ManualContentProvider
if (update == nullptr) {
const auto* manual_provider = static_cast<const ManualContentProvider*>(
content_union->GetSlotProvider(ContentProviderUnionSlot::FrontendManual));
if (manual_provider) {
auto file = manual_provider->GetEntryForVersion(update_tid, ContentRecordType::Program, *enabled_version);
if (file != nullptr) {
update = std::make_unique<NCA>(file);
}
}
}
if (update_res.update_active_version.has_value() && update_res.update_active_raw != nullptr) {
update = std::make_unique<NCA>(update_res.update_active_raw);
}
// Fallback to regular content provider if no external update was loaded
if (update == nullptr && !update_disabled) {
if (update == nullptr && !update_res.update_disabled) {
update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
}
if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr) {
if (!update_res.update_disabled && update != nullptr && update->GetExeFS() != nullptr) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
exefs = update->GetExeFS();
}
// LayeredExeFS
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
@@ -468,7 +462,8 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
return !CollectPatches(patch_dirs, build_id).empty();
}
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
std::vector<Service::DMNT::CheatEntry> PatchManager::CreateCheatList(
const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
@@ -477,35 +472,72 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
const auto& disabled = Settings::values.disabled_addons[title_id];
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
// <mod dir> / <folder> / cheats / <build id>.txt
std::vector<Core::Memory::CheatEntry> out;
// Load cheats from: <mod dir>/<folder>/cheats/<build_id>.txt
std::vector<Service::DMNT::CheatEntry> out;
for (const auto& subdir : patch_dirs) {
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) == disabled.cend()) {
if (auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); cheats_dir != nullptr) {
if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true))
std::copy(res->begin(), res->end(), std::back_inserter(out));
if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false))
std::copy(res->begin(), res->end(), std::back_inserter(out));
const auto mod_name = subdir->GetName();
// Skip entirely disabled mods
if (std::find(disabled.cbegin(), disabled.cend(), mod_name) != disabled.cend()) {
continue;
}
auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats");
if (cheats_dir == nullptr) {
continue;
}
// Try uppercase build_id first, then lowercase
std::optional<std::vector<Service::DMNT::CheatEntry>> cheat_entries;
if (auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) {
cheat_entries = std::move(res);
} else if (auto res_lower =
ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) {
cheat_entries = std::move(res_lower);
}
if (cheat_entries) {
for (auto& entry : *cheat_entries) {
// Check if this individual cheat is disabled
const std::string cheat_name = entry.definition.readable_name.data();
const std::string cheat_key = mod_name + "::" + cheat_name;
if (std::find(disabled.cbegin(), disabled.cend(), cheat_key) != disabled.cend()) {
// Individual cheat is disabled - mark it as disabled but still include it
entry.enabled = false;
}
out.push_back(entry);
}
}
}
// Uncareless user-friendly loading of patches (must start with 'cheat_')
// <mod dir> / <cheat file>.txt
for (auto const& f : load_dir->GetFiles()) {
auto const name = f->GetName();
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
std::vector<u8> data(f->GetSize());
if (f->Read(data.data(), data.size()) == data.size()) {
const Core::Memory::TextCheatParser parser;
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
std::copy(res.begin(), res.end(), std::back_inserter(out));
} else {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
}
// User-friendly cheat loading from: <mod dir>/cheat_*.txt
for (const auto& file : load_dir->GetFiles()) {
const auto& name = file->GetName();
if (!name.starts_with("cheat_")) {
continue;
}
if (std::find(disabled.cbegin(), disabled.cend(), name) != disabled.cend()) {
continue;
}
std::vector<u8> data(file->GetSize());
if (file->Read(data.data(), data.size()) != static_cast<size_t>(data.size())) {
LOG_WARNING(Common_Filesystem, "Failed to read cheat file '{}' for title_id={:016X}",
name, title_id);
continue;
}
const Service::DMNT::CheatParser parser;
auto entries =
parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
out.insert(out.end(), entries.begin(), entries.end());
}
return out;
}
@@ -597,105 +629,22 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
auto romfs = base_romfs;
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_res = GetActiveUpdate(type);
bool update_disabled = true;
std::optional<u32> enabled_version;
VirtualFile update_raw = nullptr;
bool checked_external = false;
bool checked_manual = false;
const auto* content_union = static_cast<const ContentProviderUnion*>(&content_provider);
if (content_union) {
// First, check ExternalContentProvider
const auto* external_provider = content_union->GetExternalProvider();
if (external_provider) {
const auto update_versions = external_provider->ListUpdateVersions(update_tid);
if (!update_versions.empty()) {
checked_external = true;
for (const auto& update_entry : update_versions) {
if (!IsVersionedExternalUpdateDisabled(disabled, update_entry.version)) {
update_disabled = false;
enabled_version = update_entry.version;
update_raw = external_provider->GetEntryForVersion(update_tid, type, update_entry.version);
break;
}
}
}
}
if (!checked_external) {
const auto* manual_provider = static_cast<const ManualContentProvider*>(
content_union->GetSlotProvider(ContentProviderUnionSlot::FrontendManual));
if (manual_provider) {
const auto manual_update_versions = manual_provider->ListUpdateVersions(update_tid);
if (!manual_update_versions.empty()) {
checked_manual = true;
for (const auto& update_entry : manual_update_versions) {
if (!IsVersionedExternalUpdateDisabled(disabled, update_entry.version)) {
update_disabled = false;
enabled_version = update_entry.version;
update_raw = manual_provider->GetEntryForVersion(update_tid, type, update_entry.version);
break;
}
}
}
}
}
}
if (!checked_external && !checked_manual) {
const bool nand_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (NAND)") != disabled.cend();
const bool sdmc_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (SDMC)") != disabled.cend();
const bool generic_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
if (!nand_disabled && !sdmc_disabled && !generic_disabled) {
update_disabled = false;
}
if (!update_disabled) {
update_raw = content_provider.GetEntryRaw(update_tid, type);
}
} else if (update_disabled && content_union) {
const bool nand_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (NAND)") != disabled.cend();
const bool sdmc_disabled = std::find(disabled.cbegin(), disabled.cend(), "Update (SDMC)") != disabled.cend();
if (!nand_disabled || !sdmc_disabled) {
const auto nand_sdmc_entries = content_union->ListEntriesFilterOrigin(
std::nullopt, TitleType::Update, type, update_tid);
for (const auto& [slot, entry] : nand_sdmc_entries) {
if (slot == ContentProviderUnionSlot::UserNAND ||
slot == ContentProviderUnionSlot::SysNAND) {
if (!nand_disabled) {
update_disabled = false;
update_raw = content_provider.GetEntryRaw(update_tid, type);
break;
}
} else if (slot == ContentProviderUnionSlot::SDMC) {
if (!sdmc_disabled) {
update_disabled = false;
update_raw = content_provider.GetEntryRaw(update_tid, type);
break;
}
}
}
}
}
if (!update_disabled && update_raw != nullptr && base_nca != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_raw, base_nca);
if (!update_res.update_disabled && update_res.update_active_raw != nullptr &&
base_nca != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_res.update_active_raw, base_nca);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
enabled_version.has_value() ? FormatTitleVersion(*enabled_version) :
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
LOG_INFO(
Loader, " RomFS: Update ({}) applied successfully",
update_res.update_active_version.has_value()
? FormatTitleVersion(*update_res.update_active_version)
: FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
romfs = new_nca->GetRomFS();
}
} else if (!update_disabled && packed_update_raw != nullptr && base_nca != nullptr) {
} else if (!update_res.update_disabled && packed_update_raw != nullptr && base_nca != nullptr) {
const auto new_nca = std::make_shared<NCA>(packed_update_raw, base_nca);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
@@ -712,6 +661,53 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
return romfs;
}
PatchManager::BuildID PatchManager::GetBuildID(VirtualFile update_raw) const {
BuildID build_id{};
// Get the base NCA
const auto base_nca = content_provider.GetEntry(title_id, ContentRecordType::Program);
if (base_nca == nullptr) {
return build_id;
}
// Try to get ExeFS from update first, then base
VirtualDir exefs;
const auto update_res = GetActiveUpdate();
if (!update_res.update_disabled && update_res.update_active_raw != nullptr) {
const auto update = std::make_shared<NCA>(update_res.update_active_raw, base_nca.get());
if (update->GetStatus() == Loader::ResultStatus::Success && update->GetExeFS() != nullptr) {
exefs = update->GetExeFS();
}
} else if (update_raw != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_raw, base_nca.get());
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetExeFS() != nullptr) {
exefs = new_nca->GetExeFS();
}
}
if (exefs == nullptr) {
exefs = base_nca->GetExeFS();
}
if (exefs == nullptr) {
return build_id;
}
// Try to read the main NSO header
const auto main_file = exefs->GetFile("main");
if (main_file == nullptr || main_file->GetSize() < sizeof(Loader::NSOHeader)) {
return build_id;
}
Loader::NSOHeader header{};
if (main_file->Read(reinterpret_cast<u8*>(&header), sizeof(header)) == sizeof(header)) {
build_id = header.build_id;
}
return build_id;
}
std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
if (title_id == 0) {
return {};
@@ -749,7 +745,8 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
.program_id = title_id,
.title_id = update_tid,
.source = PatchSource::External,
.numeric_version = update_entry.version};
.numeric_version = update_entry.version,
.parent_name = ""};
external_update_patches.push_back(update_patch);
}
@@ -769,7 +766,6 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
const auto update_disabled =
IsVersionedExternalUpdateDisabled(disabled, update_entry.version);
Patch update_patch = {.enabled = !update_disabled,
.name = "Update",
.version = version_str,
@@ -814,17 +810,17 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::string source_suffix;
switch (slot) {
case ContentProviderUnionSlot::UserNAND:
case ContentProviderUnionSlot::SysNAND:
source_type = PatchSource::NAND;
source_suffix = " (NAND)";
break;
case ContentProviderUnionSlot::SDMC:
source_type = PatchSource::SDMC;
source_suffix = " (SDMC)";
break;
default:
break;
case ContentProviderUnionSlot::UserNAND:
case ContentProviderUnionSlot::SysNAND:
source_type = PatchSource::NAND;
source_suffix = " (NAND)";
break;
case ContentProviderUnionSlot::SDMC:
source_type = PatchSource::SDMC;
source_suffix = " (SDMC)";
break;
default:
break;
}
std::string version_str;
@@ -895,94 +891,137 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
}
}
// Check if we have a valid build_id for cheat enumeration
const auto build_id = GetBuildID(update_raw);
const bool has_build_id =
std::any_of(build_id.begin(), build_id.end(), [](u8 b) { return b != 0; });
// General Mods (LayeredFS and IPS)
const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
if (mod_dir != nullptr) {
for (auto const& f : mod_dir->GetFiles())
if (auto const name = f->GetName(); name.starts_with("cheat_")) {
auto const mod_disabled = std::find(disabled.begin(), disabled.end(), name) != disabled.end();
out.push_back({
.enabled = !mod_disabled,
.name = name,
.version = "Cheats",
.type = PatchType::Mod,
.program_id = title_id,
.title_id = title_id,
.source = PatchSource::Unknown,
.location = f->GetFullPath(),
});
}
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
for (const auto& mod : mod_dir->GetSubdirectories()) {
for (const auto& root : std::array{mod_dir, sdmc_mod_dir}) {
if (root == nullptr)
continue;
const bool is_sdmc = (root == sdmc_mod_dir);
const std::vector<VirtualDir> mods =
is_sdmc ? std::vector{root} : root->GetSubdirectories();
for (const auto& mod : mods) {
std::string mod_name = is_sdmc ? "SDMC" : mod->GetName();
std::string types;
bool has_cheats = false;
const auto exefs_dir = FindSubdirectoryCaseless(mod, "exefs");
if (IsDirValidAndNonEmpty(exefs_dir)) {
bool ips = false;
bool ipswitch = false;
bool layeredfs = false;
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips") {
ips = true;
} else if (file->GetExtension() == "pchtxt") {
ipswitch = true;
} else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(),
file->GetName()) != EXEFS_FILE_NAMES.end()) {
layeredfs = true;
}
}
if (ips)
AppendCommaIfNotEmpty(types, "IPS");
if (ipswitch)
AppendCommaIfNotEmpty(types, "IPSwitch");
if (layeredfs)
if (is_sdmc) {
AppendCommaIfNotEmpty(types, "LayeredExeFS");
} else {
bool ips = false, ipswitch = false, layeredfs = false;
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips")
ips = true;
else if (file->GetExtension() == "pchtxt")
ipswitch = true;
else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(),
file->GetName()) != EXEFS_FILE_NAMES.end())
layeredfs = true;
}
if (ips)
AppendCommaIfNotEmpty(types, "IPS");
if (ipswitch)
AppendCommaIfNotEmpty(types, "IPSwitch");
if (layeredfs)
AppendCommaIfNotEmpty(types, "LayeredExeFS");
}
}
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs")) ||
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfslite")))
AppendCommaIfNotEmpty(types, "LayeredFS");
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "cheats")))
const auto cheats_dir = FindSubdirectoryCaseless(mod, "cheats");
if (IsDirValidAndNonEmpty(cheats_dir)) {
has_cheats = true;
AppendCommaIfNotEmpty(types, "Cheats");
}
if (has_cheats && is_sdmc)
mod_name = "Atmosphere";
if (types.empty())
continue;
const auto mod_disabled = std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
const auto mod_disabled =
std::find(disabled.begin(), disabled.end(), mod_name) != disabled.end();
out.push_back({.enabled = !mod_disabled,
.name = mod->GetName(),
.name = mod_name,
.version = types,
.type = PatchType::Mod,
.program_id = title_id,
.title_id = title_id,
.source = PatchSource::Unknown,
.location = mod->GetFullPath()});
}
}
.location = mod->GetFullPath(),
.parent_name = ""});
// SDMC mod directory (RomFS LayeredFS)
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
if (sdmc_mod_dir != nullptr) {
std::string types;
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
AppendCommaIfNotEmpty(types, "LayeredExeFS");
}
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs")) ||
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfslite"))) {
AppendCommaIfNotEmpty(types, "LayeredFS");
// Add individual cheats as sub-entries
if (has_cheats) {
std::vector<Service::DMNT::CheatEntry> cheat_entries;
if (has_build_id) {
if (auto res = ReadCheatFileFromFolder(title_id, build_id, cheats_dir, true))
cheat_entries = std::move(*res);
else if (auto res_lower =
ReadCheatFileFromFolder(title_id, build_id, cheats_dir, false))
cheat_entries = std::move(*res_lower);
}
if (!cheat_entries.empty()) {
for (const auto& cheat : cheat_entries) {
if (cheat.cheat_id <= 1 || cheat.definition.readable_name[0] == '\0')
continue;
const std::string cheat_name = cheat.definition.readable_name.data();
const std::string cheat_key =
(is_sdmc ? "SDMC" : mod->GetName()) + "::" + cheat_name;
out.push_back({
.enabled = std::find(disabled.begin(), disabled.end(), cheat_key) ==
disabled.end(),
.name = cheat_name,
.version = types,
.type = PatchType::Cheat,
.program_id = title_id,
.title_id = title_id,
.parent_name = mod_name,
.cheat_compat = CheatCompatibility::Compatible,
});
}
} else {
std::string title;
for (const auto& cheat_file : cheats_dir->GetFiles()) {
if (!cheat_file->GetName().ends_with(".txt"))
continue;
std::vector<u8> data(cheat_file->GetSize());
if (cheat_file->Read(data.data(), data.size()) == data.size()) {
const auto entries =
Service::DMNT::CheatParser{}.Parse(std::string_view(
reinterpret_cast<const char*>(data.data()), data.size()));
if (entries.size() > 1)
title = entries[1].definition.readable_name.data();
}
break;
}
out.push_back({
.enabled = false,
.name = title.empty() ? "Incompatible cheat"
: fmt::format("Incompatible cheat: {}", title),
.version = types,
.type = PatchType::Cheat,
.program_id = title_id,
.title_id = title_id,
.parent_name = mod_name,
.cheat_compat = CheatCompatibility::Incompatible,
});
}
}
if (!types.empty()) {
const auto mod_disabled =
std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
out.push_back({.enabled = !mod_disabled,
.name = "SDMC",
.version = types,
.type = PatchType::Mod,
.program_id = title_id,
.title_id = title_id,
.source = PatchSource::Unknown});
}
}
@@ -992,8 +1031,8 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
bool has_nand_dlc = false;
bool has_sdmc_dlc = false;
bool has_other_dlc = false;
const auto dlc_entries_with_origin =
content_union->ListEntriesFilterOrigin(std::nullopt, TitleType::AOC, ContentRecordType::Data);
const auto dlc_entries_with_origin = content_union->ListEntriesFilterOrigin(
std::nullopt, TitleType::AOC, ContentRecordType::Data);
dlc_match.reserve(dlc_entries_with_origin.size());
for (const auto& [slot, entry] : dlc_entries_with_origin) {
@@ -1024,20 +1063,20 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
}
switch (slot) {
case ContentProviderUnionSlot::External:
case ContentProviderUnionSlot::FrontendManual:
has_external_dlc = true;
break;
case ContentProviderUnionSlot::UserNAND:
case ContentProviderUnionSlot::SysNAND:
has_nand_dlc = true;
break;
case ContentProviderUnionSlot::SDMC:
has_sdmc_dlc = true;
break;
default:
has_other_dlc = true;
break;
case ContentProviderUnionSlot::External:
case ContentProviderUnionSlot::FrontendManual:
has_external_dlc = true;
break;
case ContentProviderUnionSlot::UserNAND:
case ContentProviderUnionSlot::SysNAND:
has_nand_dlc = true;
break;
case ContentProviderUnionSlot::SDMC:
has_sdmc_dlc = true;
break;
default:
has_other_dlc = true;
break;
}
dlc_match.push_back(entry);
}
@@ -1069,7 +1108,8 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
.type = PatchType::DLC,
.program_id = title_id,
.title_id = dlc_match.back().title_id,
.source = dlc_source});
.source = dlc_source,
.parent_name = ""});
}
return out;
+25 -4
View File
@@ -10,10 +10,10 @@
#include <memory>
#include <optional>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/memory/dmnt_cheat_types.h"
namespace Core {
class System;
@@ -23,13 +23,17 @@ namespace Service::FileSystem {
class FileSystemController;
}
namespace Service::DMNT {
struct CheatEntry;
}
namespace FileSys {
class ContentProvider;
class NCA;
class NACP;
enum class PatchType { Update, DLC, Mod };
enum class PatchType { Update, DLC, Mod, Cheat };
enum class PatchSource {
Unknown,
@@ -39,6 +43,11 @@ enum class PatchSource {
Packed,
};
enum class CheatCompatibility : u8 {
Incompatible,
Compatible,
};
struct Patch {
bool enabled;
std::string name;
@@ -49,6 +58,8 @@ struct Patch {
PatchSource source;
std::string location;
u32 numeric_version{0};
std::string parent_name;
CheatCompatibility cheat_compat{CheatCompatibility::Incompatible};
};
// A centralized class to manage patches to games.
@@ -79,7 +90,7 @@ public:
[[nodiscard]] bool HasNSOPatch(const BuildID& build_id, std::string_view name) const;
// Creates a CheatList object with all
[[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
[[nodiscard]] std::vector<Service::DMNT::CheatEntry> CreateCheatList(
const BuildID& build_id) const;
// Currently tracked RomFS patches:
@@ -90,9 +101,11 @@ public:
VirtualFile packed_update_raw = nullptr,
bool apply_layeredfs = true) const;
// Returns a vector of patches
// Returns a vector of patches including individual cheats
[[nodiscard]] std::vector<Patch> GetPatches(VirtualFile update_raw = nullptr) const;
[[nodiscard]] BuildID GetBuildID(VirtualFile update_raw = nullptr) const;
// If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
// std::nullopt
@@ -109,6 +122,14 @@ private:
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const;
struct UpdateResolution {
bool update_disabled{true};
VirtualFile update_active_raw;
std::optional<u32> update_active_version;
};
[[nodiscard]] UpdateResolution GetActiveUpdate(
ContentRecordType type = ContentRecordType::Program) const;
u64 title_id;
const Service::FileSystem::FileSystemController& fs_controller;
const ContentProvider& content_provider;
+9 -10
View File
@@ -372,17 +372,17 @@ struct KernelCore::Impl {
}
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread(KThread* existing_thread) {
if (tls_data.thread == nullptr) {
KThread* GetHostDummyThread(ThreadLocalData& t, KThread* existing_thread) {
if (t.thread == nullptr) {
auto const initialize{[](KThread* thread) {
ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
return thread;
}};
tls_data.raw_thread.emplace(system.Kernel());
tls_data.thread = existing_thread ? existing_thread : initialize(&*tls_data.raw_thread);
ASSERT(tls_data.thread != nullptr);
t.raw_thread.emplace(system.Kernel());
t.thread = existing_thread ? existing_thread : initialize(&*t.raw_thread);
ASSERT(t.thread != nullptr);
}
return tls_data.thread;
return t.thread;
}
/// Registers a CPU core thread by allocating a host thread ID for it
@@ -395,7 +395,7 @@ struct KernelCore::Impl {
/// Registers a new host thread by allocating a host thread ID for it
void RegisterHostThread(KThread* existing_thread) {
(void)GetHostDummyThread(existing_thread);
(void)GetHostDummyThread(tls_data, existing_thread);
}
[[nodiscard]] u32 GetCurrentHostThreadID() {
@@ -419,9 +419,8 @@ struct KernelCore::Impl {
}
KThread* GetCurrentEmuThread() {
if (!tls_data.current_thread)
tls_data.current_thread = GetHostDummyThread(nullptr);
return tls_data.current_thread;
auto& t = tls_data;
return t.current_thread ? t.current_thread : (t.current_thread = GetHostDummyThread(t, nullptr));
}
void SetCurrentEmuThread(KThread* thread) {
@@ -0,0 +1,238 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/dmnt/cheat_interface.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/dmnt/dmnt_results.h"
#include "core/hle/service/dmnt/dmnt_types.h"
namespace Service::DMNT {
ICheatInterface::ICheatInterface(Core::System& system_, CheatProcessManager& manager)
: ServiceFramework{system_, "dmnt:cht"}, cheat_process_manager{manager} {
// clang-format off
static const FunctionInfo functions[] = {
{65000, C<&ICheatInterface::HasCheatProcess>, "HasCheatProcess"},
{65001, C<&ICheatInterface::GetCheatProcessEvent>, "GetCheatProcessEvent"},
{65002, C<&ICheatInterface::GetCheatProcessMetadata>, "GetCheatProcessMetadata"},
{65003, C<&ICheatInterface::ForceOpenCheatProcess>, "ForceOpenCheatProcess"},
{65004, C<&ICheatInterface::PauseCheatProcess>, "PauseCheatProcess"},
{65005, C<&ICheatInterface::ResumeCheatProcess>, "ResumeCheatProcess"},
{65006, C<&ICheatInterface::ForceCloseCheatProcess>, "ForceCloseCheatProcess"},
{65100, C<&ICheatInterface::GetCheatProcessMappingCount>, "GetCheatProcessMappingCount"},
{65101, C<&ICheatInterface::GetCheatProcessMappings>, "GetCheatProcessMappings"},
{65102, C<&ICheatInterface::ReadCheatProcessMemory>, "ReadCheatProcessMemory"},
{65103, C<&ICheatInterface::WriteCheatProcessMemory>, "WriteCheatProcessMemory"},
{65104, C<&ICheatInterface::QueryCheatProcessMemory>, "QueryCheatProcessMemory"},
{65200, C<&ICheatInterface::GetCheatCount>, "GetCheatCount"},
{65201, C<&ICheatInterface::GetCheats>, "GetCheats"},
{65202, C<&ICheatInterface::GetCheatById>, "GetCheatById"},
{65203, C<&ICheatInterface::ToggleCheat>, "ToggleCheat"},
{65204, C<&ICheatInterface::AddCheat>, "AddCheat"},
{65205, C<&ICheatInterface::RemoveCheat>, "RemoveCheat"},
{65206, C<&ICheatInterface::ReadStaticRegister>, "ReadStaticRegister"},
{65207, C<&ICheatInterface::WriteStaticRegister>, "WriteStaticRegister"},
{65208, C<&ICheatInterface::ResetStaticRegisters>, "ResetStaticRegisters"},
{65209, C<&ICheatInterface::SetMasterCheat>, "SetMasterCheat"},
{65300, C<&ICheatInterface::GetFrozenAddressCount>, "GetFrozenAddressCount"},
{65301, C<&ICheatInterface::GetFrozenAddresses>, "GetFrozenAddresses"},
{65302, C<&ICheatInterface::GetFrozenAddress>, "GetFrozenAddress"},
{65303, C<&ICheatInterface::EnableFrozenAddress>, "EnableFrozenAddress"},
{65304, C<&ICheatInterface::DisableFrozenAddress>, "DisableFrozenAddress"},
};
// clang-format on
RegisterHandlers(functions);
}
ICheatInterface::~ICheatInterface() = default;
Result ICheatInterface::HasCheatProcess(Out<bool> out_has_cheat) {
LOG_INFO(CheatEngine, "called");
*out_has_cheat = cheat_process_manager.HasCheatProcess();
R_SUCCEED();
}
Result ICheatInterface::GetCheatProcessEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_INFO(CheatEngine, "called");
*out_event = &cheat_process_manager.GetCheatProcessEvent();
R_SUCCEED();
}
Result ICheatInterface::GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata) {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.GetCheatProcessMetadata(*out_metadata));
}
Result ICheatInterface::ForceOpenCheatProcess() {
LOG_INFO(CheatEngine, "called");
R_UNLESS(R_SUCCEEDED(cheat_process_manager.ForceOpenCheatProcess()), ResultCheatNotAttached);
R_SUCCEED();
}
Result ICheatInterface::PauseCheatProcess() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.PauseCheatProcess());
}
Result ICheatInterface::ResumeCheatProcess() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.ResumeCheatProcess());
}
Result ICheatInterface::ForceCloseCheatProcess() {
LOG_WARNING(CheatEngine, "(STUBBED) called");
R_RETURN(cheat_process_manager.ForceCloseCheatProcess());
}
Result ICheatInterface::GetCheatProcessMappingCount(Out<u64> out_count) {
LOG_WARNING(CheatEngine, "(STUBBED) called");
R_RETURN(cheat_process_manager.GetCheatProcessMappingCount(*out_count));
}
Result ICheatInterface::GetCheatProcessMappings(
Out<u64> out_count, u64 offset,
OutArray<Kernel::Svc::MemoryInfo, BufferAttr_HipcMapAlias> out_mappings) {
LOG_INFO(CheatEngine, "called, offset={}", offset);
R_UNLESS(!out_mappings.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.GetCheatProcessMappings(*out_count, offset, out_mappings));
}
Result ICheatInterface::ReadCheatProcessMemory(u64 address, u64 size,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer) {
LOG_DEBUG(CheatEngine, "called, address={}, size={}", address, size);
R_UNLESS(!out_buffer.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.ReadCheatProcessMemory(address, size, out_buffer));
}
Result ICheatInterface::WriteCheatProcessMemory(u64 address, u64 size,
InBuffer<BufferAttr_HipcMapAlias> buffer) {
LOG_DEBUG(CheatEngine, "called, address={}, size={}", address, size);
R_UNLESS(!buffer.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.WriteCheatProcessMemory(address, size, buffer));
}
Result ICheatInterface::QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> out_mapping,
u64 address) {
LOG_WARNING(CheatEngine, "(STUBBED) called, address={}", address);
R_RETURN(cheat_process_manager.QueryCheatProcessMemory(out_mapping, address));
}
Result ICheatInterface::GetCheatCount(Out<u64> out_count) {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.GetCheatCount(*out_count));
}
Result ICheatInterface::GetCheats(Out<u64> out_count, u64 offset,
OutArray<CheatEntry, BufferAttr_HipcMapAlias> out_cheats) {
LOG_INFO(CheatEngine, "called, offset={}", offset);
R_UNLESS(!out_cheats.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.GetCheats(*out_count, offset, out_cheats));
}
Result ICheatInterface::GetCheatById(OutLargeData<CheatEntry, BufferAttr_HipcMapAlias> out_cheat,
u32 cheat_id) {
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
R_RETURN(cheat_process_manager.GetCheatById(out_cheat, cheat_id));
}
Result ICheatInterface::ToggleCheat(u32 cheat_id) {
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
R_RETURN(cheat_process_manager.ToggleCheat(cheat_id));
}
Result ICheatInterface::AddCheat(
Out<u32> out_cheat_id, bool is_enabled,
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition) {
LOG_INFO(CheatEngine, "called, is_enabled={}", is_enabled);
R_RETURN(cheat_process_manager.AddCheat(*out_cheat_id, is_enabled, *cheat_definition));
}
Result ICheatInterface::RemoveCheat(u32 cheat_id) {
LOG_INFO(CheatEngine, "called, cheat_id={}", cheat_id);
R_RETURN(cheat_process_manager.RemoveCheat(cheat_id));
}
Result ICheatInterface::ReadStaticRegister(Out<u64> out_value, u8 register_index) {
LOG_DEBUG(CheatEngine, "called, register_index={}", register_index);
R_RETURN(cheat_process_manager.ReadStaticRegister(*out_value, register_index));
}
Result ICheatInterface::WriteStaticRegister(u8 register_index, u64 value) {
LOG_DEBUG(CheatEngine, "called, register_index={}, value={}", register_index, value);
R_RETURN(cheat_process_manager.WriteStaticRegister(register_index, value));
}
Result ICheatInterface::ResetStaticRegisters() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.ResetStaticRegisters());
}
Result ICheatInterface::SetMasterCheat(
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition) {
LOG_INFO(CheatEngine, "called, name={}, num_opcodes={}", cheat_definition->readable_name.data(),
cheat_definition->num_opcodes);
R_RETURN(cheat_process_manager.SetMasterCheat(*cheat_definition));
}
Result ICheatInterface::GetFrozenAddressCount(Out<u64> out_count) {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.GetFrozenAddressCount(*out_count));
}
Result ICheatInterface::GetFrozenAddresses(
Out<u64> out_count, u64 offset,
OutArray<FrozenAddressEntry, BufferAttr_HipcMapAlias> out_frozen_address) {
LOG_INFO(CheatEngine, "called, offset={}", offset);
R_UNLESS(!out_frozen_address.empty(), ResultCheatNullBuffer);
R_RETURN(cheat_process_manager.GetFrozenAddresses(*out_count, offset, out_frozen_address));
}
Result ICheatInterface::GetFrozenAddress(Out<FrozenAddressEntry> out_frozen_address_entry,
u64 address) {
LOG_INFO(CheatEngine, "called, address={}", address);
R_RETURN(cheat_process_manager.GetFrozenAddress(*out_frozen_address_entry, address));
}
Result ICheatInterface::EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width) {
LOG_INFO(CheatEngine, "called, address={}, width={}", address, width);
R_UNLESS(width > 0, ResultFrozenAddressInvalidWidth);
R_UNLESS(width <= sizeof(u64), ResultFrozenAddressInvalidWidth);
R_UNLESS((width & (width - 1)) == 0, ResultFrozenAddressInvalidWidth);
R_RETURN(cheat_process_manager.EnableFrozenAddress(*out_value, address, width));
}
Result ICheatInterface::DisableFrozenAddress(u64 address) {
LOG_INFO(CheatEngine, "called, address={}", address);
R_RETURN(cheat_process_manager.DisableFrozenAddress(address));
}
void ICheatInterface::InitializeCheatManager() {
LOG_INFO(CheatEngine, "called");
}
Result ICheatInterface::ReadCheatProcessMemoryUnsafe(u64 process_addr, std::span<u8> out_data,
size_t size) {
LOG_DEBUG(CheatEngine, "called, process_addr={}, size={}", process_addr, size);
R_RETURN(cheat_process_manager.ReadCheatProcessMemoryUnsafe(process_addr, &out_data, size));
}
Result ICheatInterface::WriteCheatProcessMemoryUnsafe(u64 process_addr, std::span<const u8> data,
size_t size) {
LOG_DEBUG(CheatEngine, "called, process_addr={}, size={}", process_addr, size);
R_RETURN(cheat_process_manager.WriteCheatProcessMemoryUnsafe(process_addr, &data, size));
}
Result ICheatInterface::PauseCheatProcessUnsafe() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.PauseCheatProcessUnsafe());
}
Result ICheatInterface::ResumeCheatProcessUnsafe() {
LOG_INFO(CheatEngine, "called");
R_RETURN(cheat_process_manager.ResumeCheatProcessUnsafe());
}
} // namespace Service::DMNT
@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Kernel::Svc {
struct MemoryInfo;
}
namespace Service::DMNT {
struct CheatDefinition;
struct CheatEntry;
struct CheatProcessMetadata;
struct FrozenAddressEntry;
class CheatProcessManager;
class ICheatInterface final : public ServiceFramework<ICheatInterface> {
public:
explicit ICheatInterface(Core::System& system_, CheatProcessManager& manager);
~ICheatInterface() override;
private:
Result HasCheatProcess(Out<bool> out_has_cheat);
Result GetCheatProcessEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetCheatProcessMetadata(Out<CheatProcessMetadata> out_metadata);
Result ForceOpenCheatProcess();
Result PauseCheatProcess();
Result ResumeCheatProcess();
Result ForceCloseCheatProcess();
Result GetCheatProcessMappingCount(Out<u64> out_count);
Result GetCheatProcessMappings(
Out<u64> out_count, u64 offset,
OutArray<Kernel::Svc::MemoryInfo, BufferAttr_HipcMapAlias> out_mappings);
Result ReadCheatProcessMemory(u64 address, u64 size,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
Result WriteCheatProcessMemory(u64 address, u64 size, InBuffer<BufferAttr_HipcMapAlias> buffer);
Result QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> out_mapping, u64 address);
Result GetCheatCount(Out<u64> out_count);
Result GetCheats(Out<u64> out_count, u64 offset,
OutArray<CheatEntry, BufferAttr_HipcMapAlias> out_cheats);
Result GetCheatById(OutLargeData<CheatEntry, BufferAttr_HipcMapAlias> out_cheat, u32 cheat_id);
Result ToggleCheat(u32 cheat_id);
Result AddCheat(Out<u32> out_cheat_id, bool enabled,
InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(Out<u64> out_value, u8 register_index);
Result WriteStaticRegister(u8 register_index, u64 value);
Result ResetStaticRegisters();
Result SetMasterCheat(InLargeData<CheatDefinition, BufferAttr_HipcMapAlias> cheat_definition);
Result GetFrozenAddressCount(Out<u64> out_count);
Result GetFrozenAddresses(
Out<u64> out_count, u64 offset,
OutArray<FrozenAddressEntry, BufferAttr_HipcMapAlias> out_frozen_address);
Result GetFrozenAddress(Out<FrozenAddressEntry> out_frozen_address_entry, u64 address);
Result EnableFrozenAddress(Out<u64> out_value, u64 address, u64 width);
Result DisableFrozenAddress(u64 address);
private:
void InitializeCheatManager();
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, std::span<u8> out_data, size_t size);
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, std::span<const u8> data, size_t size);
Result PauseCheatProcessUnsafe();
Result ResumeCheatProcessUnsafe();
CheatProcessManager& cheat_process_manager;
};
} // namespace Service::DMNT
+121
View File
@@ -0,0 +1,121 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <cctype>
#include <cstring>
#include <optional>
#include <string>
#include "core/hle/service/dmnt/cheat_parser.h"
#include "core/hle/service/dmnt/dmnt_types.h"
namespace Service::DMNT {
CheatParser::CheatParser() {}
CheatParser::~CheatParser() = default;
std::vector<CheatEntry> CheatParser::Parse(std::string_view data) const {
std::vector<CheatEntry> out(1);
std::optional<u64> current_entry;
for (std::size_t i = 0; i < data.size(); ++i) {
if (std::isspace(data[i])) {
continue;
}
if (data[i] == '{') {
current_entry = 0;
if (out[*current_entry].definition.num_opcodes > 0) {
return {};
}
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, '}');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (data[i] == '[') {
current_entry = out.size();
out.emplace_back();
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, ']');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (std::isxdigit(data[i])) {
if (!current_entry || out[*current_entry].definition.num_opcodes >=
out[*current_entry].definition.opcodes.size()) {
return {};
}
const auto hex = std::string(data.substr(i, 8));
if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
return {};
}
const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
value;
i += 7; // 7 because the for loop will increment by 1 more
} else {
return {};
}
}
out[0].enabled = out[0].definition.num_opcodes > 0;
out[0].cheat_id = 0;
for (u32 i = 1; i < out.size(); ++i) {
out[i].enabled = out[i].definition.num_opcodes > 0;
out[i].cheat_id = i;
}
return out;
}
std::string_view CheatParser::ExtractName(std::size_t& out_name_size, std::string_view data,
std::size_t start_index, char match) const {
auto end_index = start_index;
while (data[end_index] != match) {
++end_index;
if (end_index > data.size()) {
return {};
}
}
out_name_size = end_index - start_index;
// Clamp name if it's too big
if (out_name_size > sizeof(CheatDefinition::readable_name)) {
end_index = start_index + sizeof(CheatDefinition::readable_name);
}
return data.substr(start_index, end_index - start_index);
}
} // namespace Service::DMNT
+26
View File
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <string_view>
#include <vector>
namespace Service::DMNT {
struct CheatEntry;
class CheatParser final {
public:
CheatParser();
~CheatParser();
std::vector<CheatEntry> Parse(std::string_view data) const;
private:
std::string_view ExtractName(std::size_t& out_name_size, std::string_view data,
std::size_t start_index, char match) const;
};
} // namespace Service::DMNT
@@ -0,0 +1,599 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/arm/debug.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
#include "core/hle/service/dmnt/dmnt_results.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h"
namespace Service::DMNT {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
CheatProcessManager::CheatProcessManager(Core::System& system_)
: system{system_}, service_context{system_, "dmnt:cht"}, core_timing{system_.CoreTiming()} {
update_event = Core::Timing::CreateEvent("CheatEngine::FrameCallback",
[this](s64 time, std::chrono::nanoseconds ns_late)
-> std::optional<std::chrono::nanoseconds> {
FrameCallback(ns_late);
return std::nullopt;
});
for (size_t i = 0; i < MaxCheatCount; i++) {
ResetCheatEntry(i);
}
cheat_vm = std::make_unique<CheatVirtualMachine>(*this);
cheat_process_event = service_context.CreateEvent("CheatProcessManager::ProcessEvent");
unsafe_break_event = service_context.CreateEvent("CheatProcessManager::ProcessEvent");
}
CheatProcessManager::~CheatProcessManager() {
service_context.CloseEvent(cheat_process_event);
service_context.CloseEvent(unsafe_break_event);
core_timing.UnscheduleEvent(update_event);
}
void CheatProcessManager::SetVirtualMachine(std::unique_ptr<CheatVirtualMachine> vm) {
if (vm) {
cheat_vm = std::move(vm);
SetNeedsReloadVm(true);
}
}
bool CheatProcessManager::HasActiveCheatProcess() {
// Note: This function *MUST* be called only with the cheat lock held.
bool has_cheat_process =
cheat_process_debug_handle != InvalidHandle &&
system.ApplicationProcess()->GetProcessId() == cheat_process_metadata.process_id;
if (!has_cheat_process) {
CloseActiveCheatProcess();
}
return has_cheat_process;
}
void CheatProcessManager::CloseActiveCheatProcess() {
if (cheat_process_debug_handle != InvalidHandle) {
broken_unsafe = false;
unsafe_break_event->Signal();
core_timing.UnscheduleEvent(update_event);
// Close resources.
cheat_process_debug_handle = InvalidHandle;
// Save cheat toggles.
if (always_save_cheat_toggles || should_save_cheat_toggles) {
// TODO: save cheat toggles
should_save_cheat_toggles = false;
}
cheat_process_metadata = {};
ResetAllCheatEntries();
{
auto it = frozen_addresses_map.begin();
while (it != frozen_addresses_map.end()) {
it = frozen_addresses_map.erase(it);
}
}
cheat_process_event->Signal();
}
}
Result CheatProcessManager::EnsureCheatProcess() {
R_UNLESS(HasActiveCheatProcess(), ResultCheatNotAttached);
R_SUCCEED();
}
void CheatProcessManager::SetNeedsReloadVm(bool reload) {
needs_reload_vm = reload;
}
void CheatProcessManager::ResetCheatEntry(size_t i) {
if (i < MaxCheatCount) {
cheat_entries[i] = {};
cheat_entries[i].cheat_id = static_cast<u32>(i);
SetNeedsReloadVm(true);
}
}
void CheatProcessManager::ResetAllCheatEntries() {
for (size_t i = 0; i < MaxCheatCount; i++) {
ResetCheatEntry(i);
}
cheat_vm->ResetStaticRegisters();
}
CheatEntry* CheatProcessManager::GetCheatEntryById(size_t i) {
if (i < MaxCheatCount) {
return cheat_entries.data() + i;
}
return nullptr;
}
CheatEntry* CheatProcessManager::GetCheatEntryByReadableName(const char* readable_name) {
for (size_t i = 1; i < MaxCheatCount; i++) {
if (std::strncmp(cheat_entries[i].definition.readable_name.data(), readable_name,
sizeof(cheat_entries[i].definition.readable_name)) == 0) {
return cheat_entries.data() + i;
}
}
return nullptr;
}
CheatEntry* CheatProcessManager::GetFreeCheatEntry() {
// Check all non-master cheats for availability.
for (size_t i = 1; i < MaxCheatCount; i++) {
if (cheat_entries[i].definition.num_opcodes == 0) {
return cheat_entries.data() + i;
}
}
return nullptr;
}
bool CheatProcessManager::HasCheatProcess() {
std::scoped_lock lk(cheat_lock);
return HasActiveCheatProcess();
}
Kernel::KReadableEvent& CheatProcessManager::GetCheatProcessEvent() const {
return cheat_process_event->GetReadableEvent();
}
Result CheatProcessManager::AttachToApplicationProcess(const std::array<u8, 0x20>& build_id,
VAddr main_region_begin,
u64 main_region_size) {
std::scoped_lock lk(cheat_lock);
{
if (this->HasActiveCheatProcess()) {
this->CloseActiveCheatProcess();
}
}
cheat_process_metadata.process_id = system.ApplicationProcess()->GetProcessId();
{
const auto& page_table = system.ApplicationProcess()->GetPageTable();
cheat_process_metadata.program_id = system.GetApplicationProcessProgramID();
cheat_process_metadata.heap_extents = {
.base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(),
};
cheat_process_metadata.aslr_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(),
};
cheat_process_metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
}
{
cheat_process_metadata.main_nso_extents = {
.base = main_region_begin,
.size = main_region_size,
};
cheat_process_metadata.main_nso_build_id = build_id;
}
cheat_process_debug_handle = cheat_process_metadata.process_id;
broken_unsafe = false;
unsafe_break_event->Signal();
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, update_event);
LOG_INFO(CheatEngine, "Cheat engine started");
// Signal to our fans.
cheat_process_event->Signal();
R_SUCCEED();
}
Result CheatProcessManager::GetCheatProcessMetadata(CheatProcessMetadata& out_metadata) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
out_metadata = cheat_process_metadata;
R_SUCCEED();
}
Result CheatProcessManager::ForceOpenCheatProcess() {
// R_RETURN(AttachToApplicationProcess(false));
R_SUCCEED();
}
Result CheatProcessManager::PauseCheatProcess() {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(PauseCheatProcessUnsafe());
}
Result CheatProcessManager::PauseCheatProcessUnsafe() {
broken_unsafe = true;
unsafe_break_event->Clear();
if (system.ApplicationProcess()->IsSuspended()) {
R_SUCCEED();
}
R_RETURN(system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused));
}
Result CheatProcessManager::ResumeCheatProcess() {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(ResumeCheatProcessUnsafe());
}
Result CheatProcessManager::ResumeCheatProcessUnsafe() {
broken_unsafe = true;
unsafe_break_event->Clear();
if (!system.ApplicationProcess()->IsSuspended()) {
R_SUCCEED();
}
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
R_SUCCEED();
}
Result CheatProcessManager::ForceCloseCheatProcess() {
CloseActiveCheatProcess();
R_SUCCEED();
}
Result CheatProcessManager::GetCheatProcessMappingCount(u64& out_count) {
std::scoped_lock lk(cheat_lock);
R_TRY(this->EnsureCheatProcess());
// TODO: Call svc::QueryDebugProcessMemory
out_count = 0;
R_SUCCEED();
}
Result CheatProcessManager::GetCheatProcessMappings(
u64& out_count, u64 offset, std::span<Kernel::Svc::MemoryInfo> out_mappings) {
std::scoped_lock lk(cheat_lock);
R_TRY(this->EnsureCheatProcess());
// TODO: Call svc::QueryDebugProcessMemory
out_count = 0;
R_SUCCEED();
}
Result CheatProcessManager::ReadCheatProcessMemory(u64 process_address, u64 size,
std::span<u8> out_data) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(ReadCheatProcessMemoryUnsafe(process_address, &out_data, size));
}
Result CheatProcessManager::ReadCheatProcessMemoryUnsafe(u64 process_address, void* out_data,
size_t size) {
if (!system.ApplicationMemory().IsValidVirtualAddress(process_address)) {
std::memset(out_data, 0, size);
R_SUCCEED();
}
system.ApplicationMemory().ReadBlock(process_address, out_data, size);
R_SUCCEED();
}
Result CheatProcessManager::WriteCheatProcessMemory(u64 process_address, u64 size,
std::span<const u8> data) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_RETURN(WriteCheatProcessMemoryUnsafe(process_address, &data, size));
}
Result CheatProcessManager::WriteCheatProcessMemoryUnsafe(u64 process_address, const void* data,
size_t size) {
if (!system.ApplicationMemory().IsValidVirtualAddress(process_address)) {
R_SUCCEED();
}
if (system.ApplicationMemory().WriteBlock(process_address, data, size)) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), process_address, size);
}
R_SUCCEED();
}
Result CheatProcessManager::QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> mapping,
u64 address) {
std::scoped_lock lk(cheat_lock);
R_TRY(this->EnsureCheatProcess());
// TODO: Call svc::QueryDebugProcessMemory
R_SUCCEED();
}
Result CheatProcessManager::GetCheatCount(u64& out_count) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
out_count = std::count_if(cheat_entries.begin(), cheat_entries.end(),
[](const auto& entry) { return entry.definition.num_opcodes != 0; });
R_SUCCEED();
}
Result CheatProcessManager::GetCheats(u64& out_count, u64 offset,
std::span<CheatEntry> out_cheats) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
size_t count = 0, total_count = 0;
for (size_t i = 0; i < MaxCheatCount && count < out_cheats.size(); i++) {
if (cheat_entries[i].definition.num_opcodes) {
total_count++;
if (total_count > offset) {
out_cheats[count++] = cheat_entries[i];
}
}
}
out_count = count;
R_SUCCEED();
}
Result CheatProcessManager::GetCheatById(CheatEntry* out_cheat, u32 cheat_id) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const CheatEntry* entry = GetCheatEntryById(cheat_id);
R_UNLESS(entry != nullptr, ResultCheatUnknownId);
R_UNLESS(entry->definition.num_opcodes != 0, ResultCheatUnknownId);
*out_cheat = *entry;
R_SUCCEED();
}
Result CheatProcessManager::ToggleCheat(u32 cheat_id) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
CheatEntry* entry = GetCheatEntryById(cheat_id);
R_UNLESS(entry != nullptr, ResultCheatUnknownId);
R_UNLESS(entry->definition.num_opcodes != 0, ResultCheatUnknownId);
R_UNLESS(cheat_id != 0, ResultCheatCannotDisable);
entry->enabled = !entry->enabled;
SetNeedsReloadVm(true);
R_SUCCEED();
}
Result CheatProcessManager::AddCheat(u32& out_cheat_id, bool enabled,
const CheatDefinition& cheat_definition) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(cheat_definition.num_opcodes != 0, ResultCheatInvalid);
R_UNLESS(cheat_definition.num_opcodes <= cheat_definition.opcodes.size(), ResultCheatInvalid);
CheatEntry* new_entry = GetFreeCheatEntry();
R_UNLESS(new_entry != nullptr, ResultCheatOutOfResource);
new_entry->enabled = enabled;
new_entry->definition = cheat_definition;
SetNeedsReloadVm(true);
out_cheat_id = new_entry->cheat_id;
R_SUCCEED();
}
Result CheatProcessManager::RemoveCheat(u32 cheat_id) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(cheat_id < MaxCheatCount, ResultCheatUnknownId);
ResetCheatEntry(cheat_id);
SetNeedsReloadVm(true);
R_SUCCEED();
}
Result CheatProcessManager::ReadStaticRegister(u64& out_value, u64 register_index) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(register_index < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid);
out_value = cheat_vm->GetStaticRegister(register_index);
R_SUCCEED();
}
Result CheatProcessManager::WriteStaticRegister(u64 register_index, u64 value) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(register_index < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid);
cheat_vm->SetStaticRegister(register_index, value);
R_SUCCEED();
}
Result CheatProcessManager::ResetStaticRegisters() {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
cheat_vm->ResetStaticRegisters();
R_SUCCEED();
}
Result CheatProcessManager::SetMasterCheat(const CheatDefinition& cheat_definition) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
R_UNLESS(cheat_definition.num_opcodes != 0, ResultCheatInvalid);
R_UNLESS(cheat_definition.num_opcodes <= cheat_definition.opcodes.size(), ResultCheatInvalid);
cheat_entries[0] = {
.enabled = true,
.definition = cheat_definition,
};
SetNeedsReloadVm(true);
R_SUCCEED();
}
Result CheatProcessManager::GetFrozenAddressCount(u64& out_count) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
out_count = std::distance(frozen_addresses_map.begin(), frozen_addresses_map.end());
R_SUCCEED();
}
Result CheatProcessManager::GetFrozenAddresses(u64& out_count, u64 offset,
std::span<FrozenAddressEntry> out_frozen_address) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
u64 total_count = 0, written_count = 0;
for (const auto& [address, value] : frozen_addresses_map) {
if (written_count >= out_frozen_address.size()) {
break;
}
if (offset <= total_count) {
out_frozen_address[written_count].address = address;
out_frozen_address[written_count].value = value;
written_count++;
}
total_count++;
}
out_count = written_count;
R_SUCCEED();
}
Result CheatProcessManager::GetFrozenAddress(FrozenAddressEntry& out_frozen_address_entry,
u64 address) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const auto it = frozen_addresses_map.find(address);
R_UNLESS(it != frozen_addresses_map.end(), ResultFrozenAddressNotFound);
out_frozen_address_entry = {
.address = it->first,
.value = it->second,
};
R_SUCCEED();
}
Result CheatProcessManager::EnableFrozenAddress(u64& out_value, u64 address, u64 width) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const auto it = frozen_addresses_map.find(address);
R_UNLESS(it == frozen_addresses_map.end(), ResultFrozenAddressAlreadyExists);
FrozenAddressValue value{};
value.width = static_cast<u8>(width);
R_TRY(ReadCheatProcessMemoryUnsafe(address, &value.value, width));
frozen_addresses_map.insert({address, value});
out_value = value.value;
R_SUCCEED();
}
Result CheatProcessManager::DisableFrozenAddress(u64 address) {
std::scoped_lock lk(cheat_lock);
R_TRY(EnsureCheatProcess());
const auto it = frozen_addresses_map.find(address);
R_UNLESS(it != frozen_addresses_map.end(), ResultFrozenAddressNotFound);
frozen_addresses_map.erase(it);
R_SUCCEED();
}
u64 CheatProcessManager::HidKeysDown() const {
const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
if (hid == nullptr) {
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
return 0;
}
const auto applet_resource = hid->GetResourceManager();
if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
LOG_WARNING(CheatEngine,
"Attempted to read input state, but applet resource is not initialized!");
return 0;
}
const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
return static_cast<u64>(press_state & Core::HID::NpadButton::All);
}
void CheatProcessManager::DebugLog(u8 id, u64 value) const {
LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
}
void CheatProcessManager::CommandLog(std::string_view data) const {
LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
}
void CheatProcessManager::FrameCallback(std::chrono::nanoseconds ns_late) {
std::scoped_lock lk(cheat_lock);
if (cheat_vm == nullptr) {
LOG_DEBUG(CheatEngine, "FrameCallback: VM is null");
return;
}
if (needs_reload_vm) {
LOG_INFO(CheatEngine, "Reloading cheat VM with {} entries", cheat_entries.size());
size_t enabled_count = 0;
for (const auto& entry : cheat_entries) {
if (entry.enabled && entry.definition.num_opcodes > 0) {
enabled_count++;
LOG_INFO(CheatEngine, " Cheat '{}': {} opcodes, enabled={}",
entry.definition.readable_name.data(), entry.definition.num_opcodes,
entry.enabled);
}
}
LOG_INFO(CheatEngine, "Total enabled cheats: {}", enabled_count);
cheat_vm->LoadProgram(cheat_entries);
LOG_INFO(CheatEngine, "Cheat VM loaded, program size: {}", cheat_vm->GetProgramSize());
needs_reload_vm = false;
}
if (cheat_vm->GetProgramSize() == 0) {
return;
}
cheat_vm->Execute(cheat_process_metadata);
}
} // namespace Service::DMNT
@@ -0,0 +1,126 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <map>
#include <span>
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/dmnt/dmnt_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
#include "common/intrusive_red_black_tree.h"
namespace Core {
class System;
}
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Kernel::Svc {
struct MemoryInfo;
}
namespace Service::DMNT {
class CheatVirtualMachine;
class CheatProcessManager final {
public:
static constexpr size_t MaxCheatCount = 0x80;
static constexpr size_t MaxFrozenAddressCount = 0x80;
CheatProcessManager(Core::System& system_);
~CheatProcessManager();
void SetVirtualMachine(std::unique_ptr<CheatVirtualMachine> vm);
bool HasCheatProcess();
Kernel::KReadableEvent& GetCheatProcessEvent() const;
Result GetCheatProcessMetadata(CheatProcessMetadata& out_metadata);
Result AttachToApplicationProcess(const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
u64 main_region_size);
Result ForceOpenCheatProcess();
Result PauseCheatProcess();
Result PauseCheatProcessUnsafe();
Result ResumeCheatProcess();
Result ResumeCheatProcessUnsafe();
Result ForceCloseCheatProcess();
Result GetCheatProcessMappingCount(u64& out_count);
Result GetCheatProcessMappings(u64& out_count, u64 offset,
std::span<Kernel::Svc::MemoryInfo> out_mappings);
Result ReadCheatProcessMemory(u64 process_address, u64 size, std::span<u8> out_data);
Result ReadCheatProcessMemoryUnsafe(u64 process_address, void* out_data, size_t size);
Result WriteCheatProcessMemory(u64 process_address, u64 size, std::span<const u8> data);
Result WriteCheatProcessMemoryUnsafe(u64 process_address, const void* data, size_t size);
Result QueryCheatProcessMemory(Out<Kernel::Svc::MemoryInfo> mapping, u64 address);
Result GetCheatCount(u64& out_count);
Result GetCheats(u64& out_count, u64 offset, std::span<CheatEntry> out_cheats);
Result GetCheatById(CheatEntry* out_cheat, u32 cheat_id);
Result ToggleCheat(u32 cheat_id);
Result AddCheat(u32& out_cheat_id, bool enabled, const CheatDefinition& cheat_definition);
Result RemoveCheat(u32 cheat_id);
Result ReadStaticRegister(u64& out_value, u64 register_index);
Result WriteStaticRegister(u64 register_index, u64 value);
Result ResetStaticRegisters();
Result SetMasterCheat(const CheatDefinition& cheat_definition);
Result GetFrozenAddressCount(u64& out_count);
Result GetFrozenAddresses(u64& out_count, u64 offset,
std::span<FrozenAddressEntry> out_frozen_address);
Result GetFrozenAddress(FrozenAddressEntry& out_frozen_address_entry, u64 address);
Result EnableFrozenAddress(u64& out_value, u64 address, u64 width);
Result DisableFrozenAddress(u64 address);
u64 HidKeysDown() const;
void DebugLog(u8 id, u64 value) const;
void CommandLog(std::string_view data) const;
private:
bool HasActiveCheatProcess();
void CloseActiveCheatProcess();
Result EnsureCheatProcess();
void SetNeedsReloadVm(bool reload);
void ResetCheatEntry(size_t i);
void ResetAllCheatEntries();
CheatEntry* GetCheatEntryById(size_t i);
CheatEntry* GetCheatEntryByReadableName(const char* readable_name);
CheatEntry* GetFreeCheatEntry();
void FrameCallback(std::chrono::nanoseconds ns_late);
static constexpr u64 InvalidHandle = 0;
mutable std::mutex cheat_lock;
Kernel::KEvent* unsafe_break_event;
Kernel::KEvent* cheat_process_event;
u64 cheat_process_debug_handle = InvalidHandle;
CheatProcessMetadata cheat_process_metadata = {};
bool broken_unsafe = false;
bool needs_reload_vm = false;
std::unique_ptr<CheatVirtualMachine> cheat_vm;
bool enable_cheats_by_default = true;
bool always_save_cheat_toggles = false;
bool should_save_cheat_toggles = false;
std::array<CheatEntry, MaxCheatCount> cheat_entries = {};
// TODO: Replace with IntrusiveRedBlackTree
std::map<u64, FrozenAddressValue> frozen_addresses_map = {};
Core::System& system;
KernelHelpers::ServiceContext service_context;
std::shared_ptr<Core::Timing::EventType> update_event;
Core::Timing::CoreTiming& core_timing;
};
} // namespace Service::DMNT
@@ -1,226 +1,223 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/printf.h>
#include "common/assert.h"
#include "common/scope_exit.h"
#include "core/memory/dmnt_cheat_types.h"
#include "core/memory/dmnt_cheat_vm.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
namespace Core::Memory {
namespace Service::DMNT {
CheatVirtualMachine::CheatVirtualMachine(CheatProcessManager& cheat_manager)
: manager(cheat_manager) {}
DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks_)
: callbacks(std::move(callbacks_)) {}
CheatVirtualMachine::~CheatVirtualMachine() = default;
DmntCheatVm::~DmntCheatVm() = default;
void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
callbacks->DebugLog(static_cast<u8>(log_id), value);
void CheatVirtualMachine::DebugLog(u32 log_id, u64 value) const {
manager.DebugLog(static_cast<u8>(log_id), value);
}
void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
void CheatVirtualMachine::LogOpcode(const CheatVmOpcode& opcode) const {
if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Store Static");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
callbacks->CommandLog(
manager.CommandLog("Opcode: Store Static");
manager.CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
manager.CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type)));
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address));
callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64));
manager.CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register));
manager.CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address));
manager.CommandLog(fmt::format("Value: {:X}", store_static->value.bit64));
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Begin Conditional");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
callbacks->CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type)));
callbacks->CommandLog(
fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address));
callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64));
manager.CommandLog("Opcode: Begin Conditional");
manager.CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
manager.CommandLog(fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type)));
manager.CommandLog(fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
manager.CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address));
manager.CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64));
} else if (std::holds_alternative<EndConditionalOpcode>(opcode.opcode)) {
callbacks->CommandLog("Opcode: End Conditional");
manager.CommandLog("Opcode: End Conditional");
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) {
if (ctrl_loop->start_loop) {
callbacks->CommandLog("Opcode: Start Loop");
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
manager.CommandLog("Opcode: Start Loop");
manager.CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
manager.CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
} else {
callbacks->CommandLog("Opcode: End Loop");
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
manager.CommandLog("Opcode: End Loop");
manager.CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
}
} else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Load Register Static");
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index));
callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value));
manager.CommandLog("Opcode: Load Register Static");
manager.CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index));
manager.CommandLog(fmt::format("Value: {:X}", ldr_static->value));
} else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Load Register Memory");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index));
callbacks->CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type)));
callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address));
manager.CommandLog("Opcode: Load Register Memory");
manager.CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
manager.CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index));
manager.CommandLog(fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type)));
manager.CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg));
manager.CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address));
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Store Static to Address");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index));
manager.CommandLog("Opcode: Store Static to Address");
manager.CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
manager.CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index));
if (str_static->add_offset_reg) {
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
manager.CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
}
callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value));
manager.CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
manager.CommandLog(fmt::format("Value: {:X}", str_static->value));
} else if (auto perform_math_static =
std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Perform Static Arithmetic");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index));
callbacks->CommandLog(
manager.CommandLog("Opcode: Perform Static Arithmetic");
manager.CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
manager.CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index));
manager.CommandLog(
fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type)));
callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
manager.CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
} else if (auto begin_keypress_cond =
std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Begin Keypress Conditional");
callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
manager.CommandLog("Opcode: Begin Keypress Conditional");
manager.CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
} else if (auto perform_math_reg =
std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Perform Register Arithmetic");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index));
callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index));
manager.CommandLog("Opcode: Perform Register Arithmetic");
manager.CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
manager.CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index));
manager.CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index));
if (perform_math_reg->has_immediate) {
callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
manager.CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
} else {
callbacks->CommandLog(
fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
manager.CommandLog(fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
}
} else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Store Register to Address");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg));
manager.CommandLog("Opcode: Store Register to Address");
manager.CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
manager.CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
manager.CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
manager.CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg));
switch (str_register->ofs_type) {
case StoreRegisterOffsetType::None:
break;
case StoreRegisterOffsetType::Reg:
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
manager.CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
break;
case StoreRegisterOffsetType::Imm:
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
manager.CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
break;
case StoreRegisterOffsetType::MemReg:
callbacks->CommandLog(
manager.CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
break;
case StoreRegisterOffsetType::MemImm:
case StoreRegisterOffsetType::MemImmReg:
callbacks->CommandLog(
manager.CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
manager.CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
break;
}
} else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Begin Register Conditional");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
callbacks->CommandLog(
manager.CommandLog("Opcode: Begin Register Conditional");
manager.CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
manager.CommandLog(
fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type)));
callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
manager.CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
switch (begin_reg_cond->comp_type) {
case CompareRegisterValueType::StaticValue:
callbacks->CommandLog("Comp Type: Static Value");
callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
manager.CommandLog("Comp Type: Static Value");
manager.CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
break;
case CompareRegisterValueType::OtherRegister:
callbacks->CommandLog("Comp Type: Other Register");
callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
manager.CommandLog("Comp Type: Other Register");
manager.CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
break;
case CompareRegisterValueType::MemoryRelAddr:
callbacks->CommandLog("Comp Type: Memory Relative Address");
callbacks->CommandLog(
manager.CommandLog("Comp Type: Memory Relative Address");
manager.CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
manager.CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
break;
case CompareRegisterValueType::MemoryOfsReg:
callbacks->CommandLog("Comp Type: Memory Offset Register");
callbacks->CommandLog(
manager.CommandLog("Comp Type: Memory Offset Register");
manager.CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
manager.CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
break;
case CompareRegisterValueType::RegisterRelAddr:
callbacks->CommandLog("Comp Type: Register Relative Address");
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
manager.CommandLog("Comp Type: Register Relative Address");
manager.CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
manager.CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
break;
case CompareRegisterValueType::RegisterOfsReg:
callbacks->CommandLog("Comp Type: Register Offset Register");
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
manager.CommandLog("Comp Type: Register Offset Register");
manager.CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
manager.CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
break;
}
} else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Save or Restore Register");
callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index));
callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index));
callbacks->CommandLog(
manager.CommandLog("Opcode: Save or Restore Register");
manager.CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index));
manager.CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index));
manager.CommandLog(
fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type)));
} else if (auto save_restore_regmask =
std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Save or Restore Register Mask");
callbacks->CommandLog(
manager.CommandLog("Opcode: Save or Restore Register Mask");
manager.CommandLog(
fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type)));
for (std::size_t i = 0; i < NumRegisters; i++) {
callbacks->CommandLog(
manager.CommandLog(
fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i]));
}
} else if (auto rw_static_reg = std::get_if<ReadWriteStaticRegisterOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Read/Write Static Register");
manager.CommandLog("Opcode: Read/Write Static Register");
if (rw_static_reg->static_idx < NumReadableStaticRegisters) {
callbacks->CommandLog("Op Type: ReadStaticRegister");
manager.CommandLog("Op Type: ReadStaticRegister");
} else {
callbacks->CommandLog("Op Type: WriteStaticRegister");
manager.CommandLog("Op Type: WriteStaticRegister");
}
callbacks->CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
callbacks->CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
manager.CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
manager.CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
callbacks->CommandLog("Opcode: Debug Log");
callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id));
callbacks->CommandLog(
fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type)));
manager.CommandLog("Opcode: Debug Log");
manager.CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
manager.CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id));
manager.CommandLog(fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type)));
switch (debug_log->val_type) {
case DebugLogValueType::RegisterValue:
callbacks->CommandLog("Val Type: Register Value");
callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
manager.CommandLog("Val Type: Register Value");
manager.CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
break;
case DebugLogValueType::MemoryRelAddr:
callbacks->CommandLog("Val Type: Memory Relative Address");
callbacks->CommandLog(
manager.CommandLog("Val Type: Memory Relative Address");
manager.CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
manager.CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
break;
case DebugLogValueType::MemoryOfsReg:
callbacks->CommandLog("Val Type: Memory Offset Register");
callbacks->CommandLog(
manager.CommandLog("Val Type: Memory Offset Register");
manager.CommandLog(
fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
manager.CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
break;
case DebugLogValueType::RegisterRelAddr:
callbacks->CommandLog("Val Type: Register Relative Address");
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
manager.CommandLog("Val Type: Register Relative Address");
manager.CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
manager.CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
break;
case DebugLogValueType::RegisterOfsReg:
callbacks->CommandLog("Val Type: Register Offset Register");
callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
manager.CommandLog("Val Type: Register Offset Register");
manager.CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
manager.CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
break;
}
} else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) {
callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
manager.CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
}
}
DmntCheatVm::Callbacks::~Callbacks() = default;
bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
bool CheatVirtualMachine::DecodeNextOpcode(CheatVmOpcode& out) {
// If we've ever seen a decode failure, return false.
bool valid = decode_success;
CheatVmOpcode opcode = {};
@@ -634,7 +631,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
return valid;
}
void DmntCheatVm::SkipConditionalBlock(bool is_if) {
void CheatVirtualMachine::SkipConditionalBlock(bool is_if) {
if (condition_depth > 0) {
// We want to continue until we're out of the current block.
const std::size_t desired_depth = condition_depth - 1;
@@ -668,7 +665,7 @@ void DmntCheatVm::SkipConditionalBlock(bool is_if) {
}
}
u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
u64 CheatVirtualMachine::GetVmInt(VmInt value, u32 bit_width) {
switch (bit_width) {
case 1:
return value.bit8;
@@ -684,8 +681,8 @@ u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
}
}
u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address) {
u64 CheatVirtualMachine::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address) {
switch (mem_type) {
case MemoryAccessType::MainNso:
default:
@@ -699,7 +696,7 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
}
}
void DmntCheatVm::ResetState() {
void CheatVirtualMachine::ResetState() {
registers.fill(0);
saved_values.fill(0);
loop_tops.fill(0);
@@ -708,7 +705,7 @@ void DmntCheatVm::ResetState() {
decode_success = true;
}
bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
bool CheatVirtualMachine::LoadProgram(std::span<const CheatEntry> entries) {
// Reset opcode count.
num_opcodes = 0;
@@ -729,31 +726,31 @@ bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
return true;
}
void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
CheatVmOpcode cur_opcode{};
// Get Keys down.
u64 kDown = callbacks->HidKeysDown();
u64 kDown = manager.HidKeysDown();
callbacks->CommandLog("Started VM execution.");
callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base));
callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base));
callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
manager.CommandLog("Started VM execution.");
manager.CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base));
manager.CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base));
manager.CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
// Clear VM state.
ResetState();
// Loop until program finishes.
while (DecodeNextOpcode(cur_opcode)) {
callbacks->CommandLog(
manager.CommandLog(
fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr)));
for (std::size_t i = 0; i < NumRegisters; i++) {
callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
manager.CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
}
for (std::size_t i = 0; i < NumRegisters; i++) {
callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
manager.CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
}
LogOpcode(cur_opcode);
@@ -773,7 +770,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
store_static->bit_width);
break;
}
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
@@ -786,7 +784,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
manager.ReadCheatProcessMemoryUnsafe(src_address, &src_value,
begin_cond->bit_width);
break;
}
// Check against condition.
@@ -857,8 +856,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryReadUnsafe(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width);
manager.ReadCheatProcessMemoryUnsafe(src_address, &registers[ldr_memory->reg_index],
ldr_memory->bit_width);
break;
}
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
@@ -874,7 +873,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
str_static->bit_width);
break;
}
// Increment register if relevant.
@@ -1032,7 +1032,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
str_register->bit_width);
break;
}
@@ -1111,8 +1112,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryReadUnsafe(cond_address, &cond_value,
begin_reg_cond->bit_width);
manager.ReadCheatProcessMemoryUnsafe(cond_address, &cond_value,
begin_reg_cond->bit_width);
break;
}
}
@@ -1205,9 +1206,9 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
}
} else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
callbacks->PauseProcess();
manager.PauseCheatProcessUnsafe();
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
callbacks->ResumeProcess();
manager.ResumeCheatProcessUnsafe();
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
// Read value from memory.
u64 log_value = 0;
@@ -1254,7 +1255,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
case 2:
case 4:
case 8:
callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
manager.ReadCheatProcessMemoryUnsafe(val_address, &log_value,
debug_log->bit_width);
break;
}
}
@@ -1264,5 +1266,4 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
}
}
}
} // namespace Core::Memory
} // namespace Service::DMNT
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -6,15 +6,14 @@
#pragma once
#include <span>
#include <variant>
#include <vector>
#include <memory>
#include <fmt/printf.h>
#include "common/common_types.h"
#include "core/memory/dmnt_cheat_types.h"
#include "core/hle/service/dmnt/dmnt_types.h"
namespace Core::Memory {
namespace Service::DMNT {
class CheatProcessManager;
enum class CheatVmOpcodeType : u32 {
StoreStatic = 0,
@@ -264,25 +263,8 @@ struct CheatVmOpcode {
opcode{};
};
class DmntCheatVm {
class CheatVirtualMachine {
public:
/// Helper Type for DmntCheatVm <=> yuzu Interface
class Callbacks {
public:
virtual ~Callbacks();
virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
virtual u64 HidKeysDown() = 0;
virtual void PauseProcess() = 0;
virtual void ResumeProcess() = 0;
virtual void DebugLog(u8 id, u64 value) = 0;
virtual void CommandLog(std::string_view data) = 0;
};
static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
static constexpr std::size_t NumRegisters = 0x10;
static constexpr std::size_t NumReadableStaticRegisters = 0x80;
@@ -290,18 +272,43 @@ public:
static constexpr std::size_t NumStaticRegisters =
NumReadableStaticRegisters + NumWritableStaticRegisters;
explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks_);
~DmntCheatVm();
explicit CheatVirtualMachine(CheatProcessManager& cheat_manager);
~CheatVirtualMachine();
std::size_t GetProgramSize() const {
return this->num_opcodes;
}
bool LoadProgram(const std::vector<CheatEntry>& cheats);
bool LoadProgram(std::span<const CheatEntry> cheats);
void Execute(const CheatProcessMetadata& metadata);
u64 GetStaticRegister(std::size_t register_index) const {
return static_registers[register_index];
}
void SetStaticRegister(std::size_t register_index, u64 value) {
static_registers[register_index] = value;
}
void ResetStaticRegisters() {
static_registers = {};
}
private:
std::unique_ptr<Callbacks> callbacks;
bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock(bool is_if);
void ResetState();
// For implementing the DebugLog opcode.
void DebugLog(u32 log_id, u64 value) const;
void LogOpcode(const CheatVmOpcode& opcode) const;
static u64 GetVmInt(VmInt value, u32 bit_width);
static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address);
CheatProcessManager& manager;
std::size_t num_opcodes = 0;
std::size_t instruction_ptr = 0;
@@ -312,19 +319,5 @@ private:
std::array<u64, NumRegisters> saved_values{};
std::array<u64, NumStaticRegisters> static_registers{};
std::array<std::size_t, NumRegisters> loop_tops{};
bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock(bool is_if);
void ResetState();
// For implementing the DebugLog opcode.
void DebugLog(u32 log_id, u64 value);
void LogOpcode(const CheatVmOpcode& opcode);
static u64 GetVmInt(VmInt value, u32 bit_width);
static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address);
};
}; // namespace Core::Memory
} // namespace Service::DMNT
+26
View File
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/core.h"
#include "core/hle/service/dmnt/cheat_interface.h"
#include "core/hle/service/dmnt/cheat_process_manager.h"
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
#include "core/hle/service/dmnt/dmnt.h"
#include "core/hle/service/server_manager.h"
namespace Service::DMNT {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
auto& cheat_manager = system.GetCheatManager();
auto cheat_vm = std::make_unique<CheatVirtualMachine>(cheat_manager);
cheat_manager.SetVirtualMachine(std::move(cheat_vm));
server_manager->RegisterNamedService("dmnt:cht",
std::make_shared<ICheatInterface>(system, cheat_manager));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::DMNT
+15
View File
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
namespace Core {
class System;
};
namespace Service::DMNT {
void LoopProcess(Core::System& system);
} // namespace Service::DMNT
+26
View File
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::DMNT {
constexpr Result ResultDebuggingDisabled(ErrorModule::DMNT, 2);
constexpr Result ResultCheatNotAttached(ErrorModule::DMNT, 6500);
constexpr Result ResultCheatNullBuffer(ErrorModule::DMNT, 6501);
constexpr Result ResultCheatInvalidBuffer(ErrorModule::DMNT, 6502);
constexpr Result ResultCheatUnknownId(ErrorModule::DMNT, 6503);
constexpr Result ResultCheatOutOfResource(ErrorModule::DMNT, 6504);
constexpr Result ResultCheatInvalid(ErrorModule::DMNT, 6505);
constexpr Result ResultCheatCannotDisable(ErrorModule::DMNT, 6506);
constexpr Result ResultFrozenAddressInvalidWidth(ErrorModule::DMNT, 6600);
constexpr Result ResultFrozenAddressAlreadyExists(ErrorModule::DMNT, 6601);
constexpr Result ResultFrozenAddressNotFound(ErrorModule::DMNT, 6602);
constexpr Result ResultFrozenAddressOutOfResource(ErrorModule::DMNT, 6603);
constexpr Result ResultVirtualMachineInvalidConditionDepth(ErrorModule::DMNT, 6700);
} // namespace Service::DMNT
+55
View File
@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Service::DMNT {
struct MemoryRegionExtents {
u64 base{};
u64 size{};
};
static_assert(sizeof(MemoryRegionExtents) == 0x10, "MemoryRegionExtents is an invalid size");
struct CheatProcessMetadata {
u64 process_id{};
u64 program_id{};
MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{};
MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{};
};
static_assert(sizeof(CheatProcessMetadata) == 0x70, "CheatProcessMetadata is an invalid size");
struct CheatDefinition {
std::array<char, 0x40> readable_name;
u32 num_opcodes;
std::array<u32, 0x100> opcodes;
};
static_assert(sizeof(CheatDefinition) == 0x444, "CheatDefinition is an invalid size");
struct CheatEntry {
bool enabled;
u32 cheat_id;
CheatDefinition definition;
};
static_assert(sizeof(CheatEntry) == 0x44C, "CheatEntry is an invalid size");
static_assert(std::is_trivial_v<CheatEntry>, "CheatEntry type must be trivially copyable.");
struct FrozenAddressValue {
u64 value;
u8 width;
};
static_assert(sizeof(FrozenAddressValue) == 0x10, "FrozenAddressValue is an invalid size");
struct FrozenAddressEntry {
u64 address;
FrozenAddressValue value;
};
static_assert(sizeof(FrozenAddressEntry) == 0x18, "FrozenAddressEntry is an invalid size");
} // namespace Service::DMNT
+3 -1
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -16,6 +16,7 @@
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/btm/btm.h"
#include "core/hle/service/caps/caps.h"
#include "core/hle/service/dmnt/dmnt.h"
#include "core/hle/service/erpt/erpt.h"
#include "core/hle/service/es/es.h"
#include "core/hle/service/eupld/eupld.h"
@@ -107,6 +108,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"btdrv", &BtDrv::LoopProcess},
{"btm", &BTM::LoopProcess},
{"capsrv", &Capture::LoopProcess},
{"dmnt", &DMNT::LoopProcess},
{"erpt", &ERPT::LoopProcess},
{"es", &ES::LoopProcess},
{"eupld", &EUPLD::LoopProcess},
-288
View File
@@ -1,288 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <locale>
#include "common/hex_util.h"
#include "common/swap.h"
#include "core/arm/debug.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_process_page_table.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h"
namespace Core::Memory {
namespace {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
std::string_view ExtractName(std::size_t& out_name_size, std::string_view data,
std::size_t start_index, char match) {
auto end_index = start_index;
while (data[end_index] != match) {
++end_index;
if (end_index > data.size()) {
return {};
}
}
out_name_size = end_index - start_index;
// Clamp name if it's too big
if (out_name_size > sizeof(CheatDefinition::readable_name)) {
end_index = start_index + sizeof(CheatDefinition::readable_name);
}
return data.substr(start_index, end_index - start_index);
}
} // Anonymous namespace
StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_)
: metadata{metadata_}, system{system_} {}
StandardVmCallbacks::~StandardVmCallbacks() = default;
void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
// Return zero on invalid address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
std::memset(data, 0, size);
return;
}
system.ApplicationMemory().ReadBlock(address, data, size);
}
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
// Skip invalid memory write address
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
return;
}
if (system.ApplicationMemory().WriteBlock(address, data, size)) {
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
}
}
u64 StandardVmCallbacks::HidKeysDown() {
const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
if (hid == nullptr) {
LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
return 0;
}
const auto applet_resource = hid->GetResourceManager();
if (applet_resource == nullptr || applet_resource->GetNpad() == nullptr) {
LOG_WARNING(CheatEngine,
"Attempted to read input state, but applet resource is not initialized!");
return 0;
}
const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
return static_cast<u64>(press_state & HID::NpadButton::All);
}
void StandardVmCallbacks::PauseProcess() {
if (system.ApplicationProcess()->IsSuspended()) {
return;
}
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Paused);
}
void StandardVmCallbacks::ResumeProcess() {
if (!system.ApplicationProcess()->IsSuspended()) {
return;
}
system.ApplicationProcess()->SetActivity(Kernel::Svc::ProcessActivity::Runnable);
}
void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
}
void StandardVmCallbacks::CommandLog(std::string_view data) {
LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
}
bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
if ((in < metadata.main_nso_extents.base ||
in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
(in < metadata.heap_extents.base ||
in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
(in < metadata.alias_extents.base ||
in >= metadata.alias_extents.base + metadata.alias_extents.size) &&
(in < metadata.aslr_extents.base ||
in >= metadata.aslr_extents.base + metadata.aslr_extents.size)) {
LOG_DEBUG(CheatEngine,
"Cheat attempting to access memory at invalid address={:016X}, if this "
"persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return false; ///< Invalid addresses will hard crash
}
return true;
}
CheatParser::~CheatParser() = default;
TextCheatParser::~TextCheatParser() = default;
std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
std::vector<CheatEntry> out(1);
std::optional<u64> current_entry;
for (std::size_t i = 0; i < data.size(); ++i) {
if (::isspace(data[i])) {
continue;
}
if (data[i] == '{') {
current_entry = 0;
if (out[*current_entry].definition.num_opcodes > 0) {
return {};
}
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, '}');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (data[i] == '[') {
current_entry = out.size();
out.emplace_back();
std::size_t name_size{};
const auto name = ExtractName(name_size, data, i + 1, ']');
if (name.empty()) {
return {};
}
std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
std::min<std::size_t>(out[*current_entry].definition.readable_name.size(),
name.size()));
out[*current_entry]
.definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
'\0';
i += name_size + 1;
} else if (::isxdigit(data[i])) {
if (!current_entry || out[*current_entry].definition.num_opcodes >=
out[*current_entry].definition.opcodes.size()) {
return {};
}
const auto hex = std::string(data.substr(i, 8));
if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
return {};
}
const auto value = static_cast<u32>(std::strtoul(hex.c_str(), nullptr, 0x10));
out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
value;
i += 8;
} else {
return {};
}
}
out[0].enabled = out[0].definition.num_opcodes > 0;
out[0].cheat_id = 0;
for (u32 i = 1; i < out.size(); ++i) {
out[i].enabled = out[i].definition.num_opcodes > 0;
out[i].cheat_id = i;
}
return out;
}
CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
const std::array<u8, 0x20>& build_id_)
: vm{std::make_unique<StandardVmCallbacks>(system_, metadata)},
cheats(std::move(cheats_)), core_timing{system_.CoreTiming()}, system{system_} {
metadata.main_nso_build_id = build_id_;
}
CheatEngine::~CheatEngine() {
if (event)
core_timing.UnscheduleEvent(event);
else
LOG_ERROR(CheatEngine, "~CheatEngine before event was registered");
}
void CheatEngine::Initialize() {
event = Core::Timing::CreateEvent(
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
[this](s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
FrameCallback(ns_late);
return std::nullopt;
});
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
metadata.process_id = system.ApplicationProcess()->GetProcessId();
metadata.title_id = system.GetApplicationProcessProgramID();
const auto& page_table = system.ApplicationProcess()->GetPageTable();
metadata.heap_extents = {
.base = GetInteger(page_table.GetHeapRegionStart()),
.size = page_table.GetHeapRegionSize(),
};
metadata.aslr_extents = {
.base = GetInteger(page_table.GetAliasCodeRegionStart()),
.size = page_table.GetAliasCodeRegionSize(),
};
metadata.alias_extents = {
.base = GetInteger(page_table.GetAliasRegionStart()),
.size = page_table.GetAliasRegionSize(),
};
is_pending_reload.exchange(true);
}
void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
metadata.main_nso_extents = {
.base = main_region_begin,
.size = main_region_size,
};
}
void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
cheats = std::move(reload_cheats);
is_pending_reload.exchange(true);
}
void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
if (is_pending_reload.exchange(false)) {
vm.LoadProgram(cheats);
}
if (vm.GetProgramSize() == 0) {
return;
}
vm.Execute(metadata);
}
} // namespace Core::Memory
-88
View File
@@ -1,88 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "core/memory/dmnt_cheat_types.h"
#include "core/memory/dmnt_cheat_vm.h"
namespace Core {
class System;
}
namespace Core::Timing {
class CoreTiming;
struct EventType;
} // namespace Core::Timing
namespace Core::Memory {
class StandardVmCallbacks : public DmntCheatVm::Callbacks {
public:
StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
~StandardVmCallbacks() override;
void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
u64 HidKeysDown() override;
void PauseProcess() override;
void ResumeProcess() override;
void DebugLog(u8 id, u64 value) override;
void CommandLog(std::string_view data) override;
private:
bool IsAddressInRange(VAddr address) const;
const CheatProcessMetadata& metadata;
Core::System& system;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
// CheatList object, that can be used for execution.
class CheatParser {
public:
virtual ~CheatParser();
[[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0;
};
// CheatParser implementation that parses text files
class TextCheatParser final : public CheatParser {
public:
~TextCheatParser() override;
[[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override;
};
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
class CheatEngine final {
public:
CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
const std::array<u8, 0x20>& build_id_);
~CheatEngine();
void Initialize();
void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size);
void Reload(std::vector<CheatEntry> reload_cheats);
private:
void FrameCallback(std::chrono::nanoseconds ns_late);
DmntCheatVm vm;
CheatProcessMetadata metadata;
std::vector<CheatEntry> cheats;
std::atomic_bool is_pending_reload{false};
std::shared_ptr<Core::Timing::EventType> event;
Core::Timing::CoreTiming& core_timing;
Core::System& system;
};
} // namespace Core::Memory
-37
View File
@@ -1,37 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
namespace Core::Memory {
struct MemoryRegionExtents {
u64 base{};
u64 size{};
};
struct CheatProcessMetadata {
u64 process_id{};
u64 title_id{};
MemoryRegionExtents main_nso_extents{};
MemoryRegionExtents heap_extents{};
MemoryRegionExtents alias_extents{};
MemoryRegionExtents aslr_extents{};
std::array<u8, 0x20> main_nso_build_id{};
};
struct CheatDefinition {
std::array<char, 0x40> readable_name{};
u32 num_opcodes{};
std::array<u32, 0x100> opcodes{};
};
struct CheatEntry {
bool enabled{};
u32 cheat_id{};
CheatDefinition definition{};
};
} // namespace Core::Memory
@@ -26,48 +26,41 @@ namespace Dynarmic::A32 {
template<typename Visitor>
using ArmMatcher = Decoder::Matcher<Visitor, u32>;
template<typename Visitor>
using ArmDecodeTable = std::array<std::vector<ArmMatcher<Visitor>>, 0x1000>;
namespace detail {
inline size_t ToFastLookupIndexArm(u32 instruction) noexcept {
return ((instruction >> 4) & 0x00F) | ((instruction >> 16) & 0xFF0);
}
} // namespace detail
template<typename V>
static ArmDecodeTable<V> GetArmDecodeTable() noexcept {
ArmDecodeTable<V> table{};
for (size_t i = 0; i < table.size(); ++i) {
// PLEASE HEAP ELLIDE
for (auto const& e : std::vector<ArmMatcher<V>>{
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
template<typename V, typename ReturnType>
static std::optional<ReturnType> DecodeArm(V& visitor, u32 instruction) noexcept {
auto const make_fast_index = [](u32 a) {
return ((a >> 4) & 0x00F) | ((a >> 16) & 0xFF0);
};
struct Handler {
bool (*fn)(V&, u32);
u32 mask;
u32 expect;
};
alignas(64) static const std::array<std::vector<Handler>, 0x1000> table = [&] {
std::array<std::vector<Handler>, 0x1000> t{};
for (size_t i = 0; i < t.size(); ++i) {
#define INST(fn, name, bitstring) \
do { \
auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
if ((i & make_fast_index(mask)) == make_fast_index(expect)) { \
t[i].emplace_back([](V& visitor, u32 instruction) -> bool { \
return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
}, mask, expect); \
} \
} while (0);
#include "./arm.inc"
#undef INST
}) {
auto const expect = detail::ToFastLookupIndexArm(e.GetExpected());
auto const mask = detail::ToFastLookupIndexArm(e.GetMask());
if ((i & mask) == expect) {
table[i].push_back(e);
}
}
}
return table;
return t;
}();
for (auto const& e : table[make_fast_index(instruction)])
if ((instruction & e.mask) == e.expect)
return e.fn(visitor, instruction);
return std::nullopt;
}
template<typename V>
static std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) noexcept {
alignas(64) static const auto table = GetArmDecodeTable<V>();
const auto matches_instruction = [instruction](const auto& matcher) {
return matcher.Matches(instruction);
};
const auto& subtable = table[detail::ToFastLookupIndexArm(instruction)];
auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction);
return iter != subtable.end() ? std::optional<std::reference_wrapper<const ArmMatcher<V>>>(*iter) : std::nullopt;
}
template<typename V>
static std::optional<std::string_view> GetNameARM(u32 inst) noexcept {
static std::optional<std::string_view> GetNameArm(u32 inst) noexcept {
std::vector<std::pair<std::string_view, ArmMatcher<V>>> list = {
#define INST(fn, name, bitstring) { name, DYNARMIC_DECODER_GET_MATCHER(ArmMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)) },
#include "./arm.inc"
@@ -26,17 +26,16 @@ namespace Dynarmic::A32 {
template<typename Visitor>
using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
static std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 instruction) noexcept {
alignas(64) static const auto table = std::array{
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
template<typename V, typename ReturnType>
static std::optional<ReturnType> DecodeASIMD(V& visitor, u32 instruction) noexcept {
#define INST(fn, name, bitstring) \
do { \
auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(ASIMDMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
} while (0);
#include "./asimd.inc"
#undef INST
};
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
return matcher.Matches(instruction);
});
return iter != table.end() ? std::optional<std::reference_wrapper<const ASIMDMatcher<V>>>(*iter) : std::nullopt;
return std::nullopt;
}
template<typename V>
@@ -23,17 +23,16 @@ namespace Dynarmic::A32 {
template<typename Visitor>
using Thumb16Matcher = Decoder::Matcher<Visitor, u16>;
template<typename V>
static std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
alignas(64) static const auto table = std::array{
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)),
template<typename V, typename ReturnType>
static std::optional<ReturnType> DecodeThumb16(V& visitor, u16 instruction) {
#define INST(fn, name, bitstring) \
do { \
auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)); \
if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Thumb16Matcher, fn, name, Decoder::detail::StringToArray<16>(bitstring)); \
} while (0);
#include "./thumb16.inc"
#undef INST
};
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
return matcher.Matches(instruction);
});
return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb16Matcher<V>>>(*iter) : std::nullopt;
return std::nullopt;
}
template<typename V>
@@ -22,17 +22,16 @@ namespace Dynarmic::A32 {
template<typename Visitor>
using Thumb32Matcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
static std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
alignas(64) static const auto table = std::array{
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
template<typename V, typename ReturnType>
static std::optional<ReturnType> DecodeThumb32(V& visitor, u32 instruction) {
#define INST(fn, name, bitstring) \
do { \
auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
if ((instruction & mask) == expect) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Thumb32Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
} while (0);
#include "./thumb32.inc"
#undef INST
};
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
return matcher.Matches(instruction);
});
return iter != table.end() ? std::optional<std::reference_wrapper<const Thumb32Matcher<V>>>(*iter) : std::nullopt;
return std::nullopt;
}
template<typename V>
@@ -23,32 +23,18 @@ namespace Dynarmic::A32 {
template<typename Visitor>
using VFPMatcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
static std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
using Table = std::vector<VFPMatcher<V>>;
alignas(64) static const struct Tables {
Table unconditional;
Table conditional;
} tables = []() {
Table list = {
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
template<typename V, typename ReturnType>
static std::optional<ReturnType> DecodeVFP(V& visitor, u32 instruction) {
bool const i_uncond = (instruction & 0xF0000000) == 0xF0000000;
#define INST(fn, name, bitstring) \
do { \
auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
bool const m_uncond = (mask & 0xF0000000) == 0xF0000000; \
if ((instruction & mask) == expect && m_uncond == i_uncond) return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(VFPMatcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
} while (0);
#include "./vfp.inc"
#undef INST
};
auto const it = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
return (matcher.GetMask() & 0xF0000000) == 0xF0000000;
});
return Tables{
Table{list.begin(), it},
Table{it, list.end()},
};
}();
const bool is_unconditional = (instruction & 0xF0000000) == 0xF0000000;
const Table& table = is_unconditional ? tables.unconditional : tables.conditional;
auto iter = std::find_if(table.begin(), table.end(), [instruction](const auto& matcher) {
return matcher.Matches(instruction);
});
return iter != table.end() ? std::optional<std::reference_wrapper<const VFPMatcher<V>>>(*iter) : std::nullopt;
return std::nullopt;
}
template<typename V>
@@ -41,12 +41,12 @@ void TranslateArm(IR::Block& block, LocationDescriptor descriptor, TranslateCall
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
ticks_for_instruction = tcb->GetTicksForCode(false, arm_pc, *arm_instruction);
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(*arm_instruction)) {
should_continue = vfp_decoder->get().call(visitor, *arm_instruction);
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(*arm_instruction)) {
should_continue = asimd_decoder->get().call(visitor, *arm_instruction);
} else if (const auto decoder = DecodeArm<TranslatorVisitor>(*arm_instruction)) {
should_continue = decoder->get().call(visitor, *arm_instruction);
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, *arm_instruction)) {
should_continue = *vfp_decoder;
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, *arm_instruction)) {
should_continue = *asimd_decoder;
} else if (const auto decoder = DecodeArm<TranslatorVisitor, bool>(visitor, *arm_instruction)) {
should_continue = *decoder;
} else {
should_continue = visitor.arm_UDF();
}
@@ -88,12 +88,12 @@ bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descript
const u64 ticks_for_instruction = 1;
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(arm_instruction)) {
should_continue = vfp_decoder->get().call(visitor, arm_instruction);
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(arm_instruction)) {
should_continue = asimd_decoder->get().call(visitor, arm_instruction);
} else if (const auto decoder = DecodeArm<TranslatorVisitor>(arm_instruction)) {
should_continue = decoder->get().call(visitor, arm_instruction);
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, arm_instruction)) {
should_continue = *vfp_decoder;
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, arm_instruction)) {
should_continue = *asimd_decoder;
} else if (const auto decoder = DecodeArm<TranslatorVisitor, bool>(visitor, arm_instruction)) {
should_continue = *decoder;
} else {
should_continue = visitor.arm_UDF();
}
@@ -126,24 +126,24 @@ void TranslateThumb(IR::Block& block, LocationDescriptor descriptor, TranslateCa
if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) {
if (is_thumb_16) {
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
if (const auto decoder = DecodeThumb16<TranslatorVisitor, bool>(visitor, u16(thumb_instruction))) {
should_continue = *decoder;
} else {
should_continue = visitor.thumb16_UDF();
}
} else {
if (MaybeVFPOrASIMDInstruction(thumb_instruction)) {
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) {
should_continue = vfp_decoder->get().call(visitor, thumb_instruction);
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) {
should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction));
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
should_continue = decoder->get().call(visitor, thumb_instruction);
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
should_continue = *vfp_decoder;
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, ConvertASIMDInstruction(thumb_instruction))) {
should_continue = *asimd_decoder;
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
should_continue = *decoder;
} else {
should_continue = visitor.thumb32_UDF();
}
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
should_continue = decoder->get().call(visitor, thumb_instruction);
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
should_continue = *decoder;
} else {
should_continue = visitor.thumb32_UDF();
}
@@ -187,25 +187,25 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri
const u64 ticks_for_instruction = 1;
if (is_thumb_16) {
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
if (const auto decoder = DecodeThumb16<TranslatorVisitor, bool>(visitor, u16(thumb_instruction))) {
should_continue = *decoder;
} else {
should_continue = visitor.thumb16_UDF();
}
} else {
thumb_instruction = mcl::bit::swap_halves_32(thumb_instruction);
if (MaybeVFPOrASIMDInstruction(thumb_instruction)) {
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) {
should_continue = vfp_decoder->get().call(visitor, thumb_instruction);
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) {
should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction));
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
should_continue = decoder->get().call(visitor, thumb_instruction);
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
should_continue = *vfp_decoder;
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor, bool>(visitor, ConvertASIMDInstruction(thumb_instruction))) {
should_continue = *asimd_decoder;
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
should_continue = *decoder;
} else {
should_continue = visitor.thumb32_UDF();
}
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
should_continue = decoder->get().call(visitor, thumb_instruction);
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
should_continue = *decoder;
} else {
should_continue = visitor.thumb32_UDF();
}
@@ -26,45 +26,37 @@ namespace Dynarmic::A64 {
template<typename Visitor>
using Matcher = Decoder::Matcher<Visitor, u32>;
template<typename Visitor>
using DecodeTable = std::array<std::vector<Matcher<Visitor>>, 0x1000>;
namespace detail {
inline size_t ToFastLookupIndex(u32 instruction) {
return ((instruction >> 10) & 0x00F) | ((instruction >> 18) & 0xFF0);
}
} // namespace detail
template<typename V>
inline DecodeTable<V> GetDecodeTable() {
DecodeTable<V> table{};
for (size_t i = 0; i < table.size(); ++i) {
// PLEASE HEAP ELLIDE
for (auto const& e : std::vector<Matcher<V>>{
#define INST(fn, name, bitstring) DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)),
template<typename V, typename ReturnType>
static std::optional<ReturnType> Decode(V& visitor, u32 instruction) noexcept {
auto const make_fast_index = [](u32 a) {
return ((a >> 10) & 0x00F) | ((a >> 18) & 0xFF0);
};
struct Handler {
bool (*fn)(V&, u32);
u32 mask;
u32 expect;
};
alignas(64) static const std::array<std::vector<Handler>, 0x1000> table = [&] {
std::array<std::vector<Handler>, 0x1000> t{};
for (size_t i = 0; i < t.size(); ++i) {
#define INST(fn, name, bitstring) \
do { \
auto const [mask, expect] = DYNARMIC_DECODER_GET_MATCHER(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
if ((i & make_fast_index(mask)) == make_fast_index(expect)) { \
t[i].emplace_back([](V& visitor, u32 instruction) -> bool { \
return DYNARMIC_DECODER_GET_MATCHER_FUNCTION(Matcher, fn, name, Decoder::detail::StringToArray<32>(bitstring)); \
}, mask, expect); \
} \
} while (0);
#include "./a64.inc"
#undef INST
}) {
const auto expect = detail::ToFastLookupIndex(e.GetExpected());
const auto mask = detail::ToFastLookupIndex(e.GetMask());
if ((i & mask) == expect)
table[i].push_back(e);
}
}
return table;
}
/// In practice it must always suceed, otherwise something else unrelated would have gone awry
template<typename V>
inline std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
alignas(64) static const auto table = GetDecodeTable<V>();
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
auto iter = std::find_if(subtable.begin(), subtable.end(), [instruction](const auto& matcher) {
return matcher.Matches(instruction);
});
return iter != subtable.end()
? std::optional{ std::reference_wrapper<const Matcher<V>>(*iter) }
: std::nullopt;
return t;
}();
for (auto const& e : table[make_fast_index(instruction)])
if ((instruction & e.mask) == e.expect)
return e.fn(visitor, instruction);
return std::nullopt;
}
template<typename V>
@@ -24,9 +24,8 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu
do {
const u64 pc = visitor.ir.current_location->PC();
if (const auto instruction = memory_read_code(pc)) {
auto decoder = Decode<TranslatorVisitor>(*instruction);
if (decoder) {
should_continue = decoder->get().call(visitor, *instruction);
if (auto decoder = Decode<TranslatorVisitor, bool>(visitor, *instruction)) {
should_continue = *decoder;
} else {
should_continue = visitor.RaiseException(Exception::UnallocatedEncoding);
}
@@ -47,13 +46,9 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu
bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction) {
TranslatorVisitor visitor{block, descriptor, {}};
bool should_continue = true;
auto const decoder = Decode<TranslatorVisitor>(instruction);
if (decoder) {
should_continue = decoder->get().call(visitor, instruction);
} else {
should_continue = false;
}
bool should_continue = false;
if (auto const decoder = Decode<TranslatorVisitor, bool>(visitor, instruction))
should_continue = *decoder;
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
block.CycleCount()++;
@@ -8,31 +8,25 @@
#pragma once
#include <algorithm>
#include <array>
#include <tuple>
#include <utility>
#include "common/assert.h"
#include "dynarmic/mcl/bit.hpp"
#include "dynarmic/mcl/function_info.hpp"
namespace Dynarmic::Decoder {
namespace detail {
template<size_t N>
inline consteval std::array<char, N> StringToArray(const char (&str)[N + 1]) {
std::array<char, N> result{};
for (size_t i = 0; i < N; i++) {
result[i] = str[i];
}
return result;
inline consteval std::array<char, N> StringToArray(const char (&s)[N + 1]) {
std::array<char, N> r{};
for (size_t i = 0; i < N; i++)
r[i] = s[i];
return r;
}
/**
* Helper functions for the decoders.
*
* @tparam MatcherT The type of the Matcher to use.
*/
/// @brief Helper functions for the decoders.
/// @tparam MatcherT The type of the Matcher to use.
template<class MatcherT>
struct detail {
using opcode_type = typename MatcherT::opcode_type;
@@ -40,17 +34,15 @@ struct detail {
static constexpr size_t opcode_bitsize = mcl::bitsizeof<opcode_type>;
/**
* Generates the mask and the expected value after masking from a given bitstring.
* A '0' in a bitstring indicates that a zero must be present at that bit position.
* A '1' in a bitstring indicates that a one must be present at that bit position.
*/
/// @brief Generates the mask and the expected value after masking from a given bitstring.
/// A '0' in a bitstring indicates that a zero must be present at that bit position.
/// A '1' in a bitstring indicates that a one must be present at that bit position.
#ifdef __clang__
static constexpr auto GetMaskAndExpect(std::array<char, opcode_bitsize> bitstring) {
#else
static consteval auto GetMaskAndExpect(std::array<char, opcode_bitsize> bitstring) {
#endif
const auto one = static_cast<opcode_type>(1);
const auto one = opcode_type(1);
opcode_type mask = 0, expect = 0;
for (size_t i = 0; i < opcode_bitsize; i++) {
const size_t bit_position = opcode_bitsize - i - 1;
@@ -101,7 +93,7 @@ struct detail {
}
#if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__)
// Avoids a MSVC ICE, and avoids Android NDK issue.
ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
DEBUG_ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
#endif
return std::make_tuple(masks, shifts);
}
@@ -109,65 +101,32 @@ struct detail {
/// @brief This struct's Make member function generates a lambda which decodes an instruction
/// based on the provided arg_masks and arg_shifts. The Visitor member function to call is
/// provided as a template argument.
template<typename FnT>
struct VisitorCaller;
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4800) // forcing value to bool 'true' or 'false' (performance warning)
#endif
template<typename V, typename... Args, typename ReturnType>
struct VisitorCaller<ReturnType (V::*)(Args...)> {
template<size_t... iota>
static constexpr auto Make(std::integer_sequence<size_t, iota...>,
ReturnType (V::*const fn)(Args...),
const std::array<opcode_type, sizeof...(iota)> arg_masks,
const std::array<size_t, sizeof...(iota)> arg_shifts) {
static_assert(std::is_same_v<visitor_type, V>, "Member function is not from Matcher's Visitor");
return [fn, arg_masks, arg_shifts](V& v, opcode_type instruction) {
(void)instruction;
(void)arg_masks;
(void)arg_shifts;
return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
};
}
};
template<typename V, typename... Args, typename ReturnType>
struct VisitorCaller<ReturnType (V::*)(Args...) const> {
template<size_t... iota>
static constexpr auto Make(std::integer_sequence<size_t, iota...>,
ReturnType (V::*const fn)(Args...) const,
const std::array<opcode_type, sizeof...(iota)> arg_masks,
const std::array<size_t, sizeof...(iota)> arg_shifts) {
static_assert(std::is_same_v<visitor_type, const V>, "Member function is not from Matcher's Visitor");
return [fn, arg_masks, arg_shifts](const V& v, opcode_type instruction) {
(void)instruction;
(void)arg_masks;
(void)arg_shifts;
return (v.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
};
template<typename V, typename ReturnType, typename... Args>
struct VisitorCaller {
template<std::size_t... iota>
static inline constexpr auto Invoke(std::index_sequence<iota...>, V& visitor, [[maybe_unused]] opcode_type instruction, ReturnType (V::*const fn)(Args...), [[maybe_unused]] const std::array<opcode_type, sizeof...(Args)> arg_masks, [[maybe_unused]] const std::array<size_t, sizeof...(Args)> arg_shifts) {
return (visitor.*fn)(Args((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
}
};
#ifdef _MSC_VER
# pragma warning(pop)
#endif
/// @brief Creates a matcher that can match and parse instructions based on bitstring.
/// See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
template<auto bitstring, typename F>
static constexpr auto GetMatcher(F fn) {
constexpr size_t args_count = mcl::parameter_count_v<F>;
constexpr auto mask = std::get<0>(GetMaskAndExpect(bitstring));
constexpr auto expect = std::get<1>(GetMaskAndExpect(bitstring));
constexpr auto arg_masks = std::get<0>(GetArgInfo<args_count>(bitstring));
constexpr auto arg_shifts = std::get<1>(GetArgInfo<args_count>(bitstring));
const auto proxy_fn = VisitorCaller<F>::Make(std::make_index_sequence<args_count>(), fn, arg_masks, arg_shifts);
return MatcherT(mask, expect, proxy_fn);
template<auto bitstring, typename V, typename ReturnType, typename... Args>
static inline constexpr auto GetMatcherFunction(V& visitor, [[maybe_unused]] opcode_type instruction, ReturnType (V::*const fn)(Args...)) {
constexpr auto arg_masks = std::get<0>(GetArgInfo<sizeof...(Args)>(bitstring));
constexpr auto arg_shifts = std::get<1>(GetArgInfo<sizeof...(Args)>(bitstring));
return VisitorCaller<V, ReturnType, Args...>::Invoke(std::index_sequence_for<Args...>(), visitor, instruction, fn, arg_masks, arg_shifts);
}
};
#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcher<bitstring>(&V::fn)
#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::GetMaskAndExpect(bitstring)
#define DYNARMIC_DECODER_GET_MATCHER_FUNCTION(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcherFunction<bitstring, V>(visitor, instruction, &V::fn)
} // namespace detail
} // namespace Dynarmic::Decoder
@@ -15,50 +15,41 @@
namespace Dynarmic::Decoder {
/// Generic instruction handling construct.
/// @tparam Visitor An arbitrary visitor type that will be passed through
/// to the function being handled. This type must be the
/// type of the first parameter in a handler function.
/// @tparam OpcodeType Type representing an opcode. This must be the
/// type of the second parameter in a handler function.
template<typename Visitor, typename OpcodeType>
/// @tparam V An arbitrary visitor type that will be passed through
/// to the function being handled. This type must be the
/// type of the first parameter in a handler function.
/// @tparam T Type representing an opcode. This must be the
/// type of the second parameter in a handler function.
template<typename V, typename T>
class Matcher {
public:
using opcode_type = OpcodeType;
using visitor_type = Visitor;
using handler_return_type = typename Visitor::instruction_return_type;
using handler_function = std::function<handler_return_type(Visitor&, opcode_type)>;
Matcher(opcode_type mask, opcode_type expected, handler_function func)
: mask{mask}, expected{expected}, fn{std::move(func)} {}
using opcode_type = T;
using visitor_type = V;
/// Gets the mask for this instruction.
inline opcode_type GetMask() const noexcept {
constexpr Matcher(T mask, T expected) noexcept
: mask{mask}
, expected{expected}
{}
/// @brief Gets the mask for this instruction.
constexpr inline T GetMask() const noexcept {
return mask;
}
/// Gets the expected value after masking for this instruction.
inline opcode_type GetExpected() const noexcept {
/// @brief Gets the expected value after masking for this instruction.
constexpr inline T GetExpected() const noexcept {
return expected;
}
/// Tests to see if the given instruction is the instruction this matcher represents.
/// @brief Tests to see if the given instruction is the instruction this matcher represents.
/// @param instruction The instruction to test
/// @returns true if the given instruction matches.
inline bool Matches(opcode_type instruction) const noexcept {
constexpr inline bool Matches(T instruction) const noexcept {
return (instruction & mask) == expected;
}
/// Calls the corresponding instruction handler on visitor for this type of instruction.
/// @param v The visitor to use
/// @param instruction The instruction to decode.
inline handler_return_type call(Visitor& v, opcode_type instruction) const noexcept {
ASSERT(Matches(instruction));
return fn(v, instruction);
}
private:
opcode_type mask;
opcode_type expected;
handler_function fn;
T mask;
T expected;
};
} // namespace Dynarmic::Decoder
+2 -3
View File
@@ -27,9 +27,8 @@ class Imm {
public:
static constexpr size_t bit_size = bit_size_;
explicit Imm(u32 value)
: value(value) {
ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected");
explicit Imm(u32 value) : value(value) {
DEBUG_ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected");
}
template<typename T = u32>
+1 -1
View File
@@ -51,7 +51,7 @@ TEST_CASE("ASIMD Decoder: Ensure table order correctness", "[decode][a32][.]") {
const bool iserr = is_decode_error(*iter, instruction);
const auto alternative = std::find_if(table.cbegin(), iter, [instruction](const auto& m) {
return m.Matches(instruction);
return (instruction & mask) == expect;
});
const bool altiserr = is_decode_error(*alternative, instruction);
+6 -6
View File
@@ -43,12 +43,12 @@
using namespace Dynarmic;
std::string_view GetNameOfA32Instruction(u32 instruction) {
if (auto const vfp_decoder = A32::DecodeVFP<A32::TranslatorVisitor>(instruction))
return *A32::GetNameVFP<A32::TranslatorVisitor>(instruction);
else if (auto const asimd_decoder = A32::DecodeASIMD<A32::TranslatorVisitor>(instruction))
return *A32::GetNameASIMD<A32::TranslatorVisitor>(instruction);
else if (auto const decoder = A32::DecodeArm<A32::TranslatorVisitor>(instruction))
return *A32::GetNameARM<A32::TranslatorVisitor>(instruction);
if (auto const vfp_decoder = A32::GetNameVFP<A32::TranslatorVisitor>(instruction))
return *vfp_decoder;
else if (auto const asimd_decoder = A32::GetNameASIMD<A32::TranslatorVisitor>(instruction))
return *asimd_decoder;
else if (auto const decoder = A32::GetNameArm<A32::TranslatorVisitor>(instruction))
return *decoder;
return "<null>";
}
+2 -2
View File
@@ -811,7 +811,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
auto& flags = maxwell3d->dirty.flags;
u32 enabled_mask = enabled_vertex_buffers_mask;
HostBindings<Buffer> bindings{};
u32 last_index = std::numeric_limits<u32>::max();
u32 last_index = (std::numeric_limits<u32>::max)();
const auto flush_bindings = [&]() {
if (bindings.buffers.empty()) {
return;
@@ -819,7 +819,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
bindings.max_index = bindings.min_index + static_cast<u32>(bindings.buffers.size());
runtime.BindVertexBuffers(bindings);
bindings = HostBindings<Buffer>{};
last_index = std::numeric_limits<u32>::max();
last_index = (std::numeric_limits<u32>::max)();
};
while (enabled_mask != 0) {
const u32 index = std::countr_zero(enabled_mask);
@@ -951,7 +951,7 @@ private:
runtime.View3DRegs([this](Maxwell3D& maxwell3d) {
buffers_count = 0;
out_topology = maxwell3d.draw_manager.draw_state.topology;
patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
patch_vertices = (std::max)(maxwell3d.regs.patch_vertices, 1U);
if (out_topology == Maxwell3D::Regs::PrimitiveTopology::Patches) {
switch (maxwell3d.regs.tessellation.params.output_primitives.Value()) {
case Maxwell3D::Regs::Tessellation::OutputPrimitives::Points:
@@ -1145,7 +1145,7 @@ public:
}
new_query->stride = 1;
runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) {
new_query->patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U);
new_query->patch_vertices = (std::max)(maxwell3d.regs.patch_vertices, 1U);
for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) {
const auto& tf = maxwell3d.regs.transform_feedback;
if (tf.buffers[i].enable == 0) {
@@ -5,6 +5,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <utility>
@@ -106,18 +108,39 @@ void ConfigurePerGameAddons::OnItemChanged(QStandardItem* item) {
void ConfigurePerGameAddons::ApplyConfiguration() {
std::vector<std::string> disabled_addons;
for (const auto& item : list_items) {
const auto disabled = item.front()->checkState() == Qt::Unchecked;
if (disabled) {
QVariant userData = item.front()->data(Qt::UserRole);
// Helper function to recursively collect disabled items
std::function<void(QStandardItem*)> collect_disabled = [&](QStandardItem* item) {
if (item == nullptr) {
return;
}
// Check if this item is disabled
if (item->isCheckable() && item->checkState() == Qt::Unchecked) {
QVariant userData = item->data(Qt::UserRole);
if (userData.isValid() && userData.canConvert<quint32>() &&
item.front()->text() == QStringLiteral("Update")) {
quint32 numeric_version = userData.toUInt();
item->text() == QStringLiteral("Update")) {
const quint32 numeric_version = userData.toUInt();
disabled_addons.push_back(fmt::format("Update@{}", numeric_version));
} else {
disabled_addons.push_back(item.front()->text().toStdString());
// Use the stored key from UserRole, falling back to text
const auto key = userData.toString();
if (!key.isEmpty()) {
disabled_addons.push_back(key.toStdString());
} else {
disabled_addons.push_back(item->text().toStdString());
}
}
}
// Process children (for cheats under mods)
for (int row = 0; row < item->rowCount(); ++row) {
collect_disabled(item->child(row, 0));
}
};
// Process all root items
for (int row = 0; row < item_model->rowCount(); ++row) {
collect_disabled(item_model->item(row, 0));
}
auto current = Settings::values.disabled_addons[title_id];
@@ -314,19 +337,40 @@ void ConfigurePerGameAddons::LoadConfiguration() {
bool has_enabled_update = false;
// Map to store parent items for mods (for adding cheat children)
std::map<std::string, QStandardItem*> mod_items;
for (const auto& patch : patches) {
const auto name = QString::fromStdString(patch.name);
// For cheats, we need to use the full key (parent::name) for storage
std::string storage_key;
if (patch.type == FileSys::PatchType::Cheat && !patch.parent_name.empty()) {
storage_key = patch.parent_name + "::" + patch.name;
} else {
storage_key = patch.name;
}
auto* const first_item = new QStandardItem;
first_item->setText(name);
first_item->setCheckable(true);
const bool is_external_update = patch.type == FileSys::PatchType::Update &&
patch.source == FileSys::PatchSource::External &&
patch.numeric_version != 0;
const bool is_mod = patch.type == FileSys::PatchType::Mod;
const bool is_incompatible_cheat =
patch.type == FileSys::PatchType::Cheat &&
patch.cheat_compat != FileSys::CheatCompatibility::Compatible;
if (is_incompatible_cheat) {
first_item->setCheckable(false);
first_item->setEnabled(false);
} else {
first_item->setCheckable(true);
first_item->setData(QString::fromStdString(storage_key), Qt::UserRole);
}
if (is_external_update) {
first_item->setData(static_cast<quint32>(patch.numeric_version), NUMERIC_VERSION);
} else if (is_mod) {
@@ -334,16 +378,18 @@ void ConfigurePerGameAddons::LoadConfiguration() {
}
bool patch_disabled = false;
if (is_external_update) {
std::string disabled_key = fmt::format("Update@{}", patch.numeric_version);
patch_disabled =
std::find(disabled.begin(), disabled.end(), disabled_key) != disabled.end();
} else {
patch_disabled =
std::find(disabled.begin(), disabled.end(), name.toStdString()) != disabled.end();
if (!is_incompatible_cheat) {
if (is_external_update) {
std::string disabled_key = fmt::format("Update@{}", patch.numeric_version);
patch_disabled =
std::find(disabled.begin(), disabled.end(), disabled_key) != disabled.end();
} else {
patch_disabled =
std::find(disabled.begin(), disabled.end(), storage_key) != disabled.end();
}
}
bool should_enable = !patch_disabled;
bool should_enable = !patch_disabled && !is_incompatible_cheat;
if (patch.type == FileSys::PatchType::Update) {
if (should_enable) {
@@ -356,12 +402,37 @@ void ConfigurePerGameAddons::LoadConfiguration() {
update_items.push_back(first_item);
}
first_item->setCheckState(should_enable ? Qt::Checked : Qt::Unchecked);
if (!is_incompatible_cheat) {
first_item->setCheckState(should_enable ? Qt::Checked : Qt::Unchecked);
}
list_items.push_back(QList<QStandardItem*>{
first_item, new QStandardItem{QString::fromStdString(patch.version)}});
item_model->appendRow(list_items.back());
auto* const version_item = new QStandardItem{QString::fromStdString(patch.version)};
if (is_incompatible_cheat) {
version_item->setEnabled(false);
}
if (patch.type == FileSys::PatchType::Cheat && !patch.parent_name.empty()) {
// This is a cheat - add as child of its parent mod
auto parent_it = mod_items.find(patch.parent_name);
if (parent_it != mod_items.end()) {
parent_it->second->appendRow(QList<QStandardItem*>{first_item, version_item});
} else {
// Parent not found (shouldn't happen), add to root
list_items.push_back(QList<QStandardItem*>{first_item, version_item});
item_model->appendRow(list_items.back());
}
} else {
// This is a top-level item (Update, Mod, DLC)
list_items.push_back(QList<QStandardItem*>{first_item, version_item});
item_model->appendRow(list_items.back());
// Store mod items for later cheat attachment
if (patch.type == FileSys::PatchType::Mod) {
mod_items[patch.name] = first_item;
}
}
}
tree_view->expandAll();
tree_view->resizeColumnToContents(1);
}