mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-10-12 14:28:54 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb07a431b8 | |||
| 3ae3e1fdfc | |||
| 87e65c5192 | |||
| 95401eeed5 | |||
| 05f14b897c |
+19
-25
@@ -169,31 +169,6 @@ 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)
|
||||
@@ -713,6 +688,25 @@ 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
|
||||
|
||||
Vendored
+10
-16
@@ -6038,53 +6038,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL غير متوفر!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation>OpenGL لا يتم دعم السياقات المشتركة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation>OpenGL لم يتم تجميع إيدن بدعم</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>OpenGL حدث خطأ أثناء تهيئة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>OpenGL 4.6 حدث خطأ أثناء تهيئة</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>أو قد لا يكون لديك أحدث برنامج تشغيل للرسومات OpenGL 4.6 قد لا تدعم بطاقة الرسومات الخاصة بك.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>قد لا تدعم وحدة معالجة الرسومات لديك ملحقًا واحدًا أو أكثر من ملحقات OpenGL المطلوبة. يُرجى التأكد من تثبيت أحدث برنامج تشغيل للرسومات.<br><br>GL Renderer:<br>1%<br><br>إضافات غير مدعومة: <br>2%</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation>هذا الإصدار لا يدعم OpenGL.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+11
-17
@@ -494,7 +494,7 @@ This is mainly a debug option and shouldn't be disabled.</source>
|
||||
<message>
|
||||
<location filename="../../src/qt_common/config/shared_translation.cpp" line="66"/>
|
||||
<source>Memory Layout</source>
|
||||
<translation>Distribució de memòria</translation>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/qt_common/config/shared_translation.cpp" line="67"/>
|
||||
@@ -5972,53 +5972,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL no disponible!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Error al inicialitzar OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>Error inicialitzant OpenGL 4.6!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>La seva GPU no suporta OpenGL 4.6, o no té instal·lats els últims controladors gràfics.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>És possible que la seva GPU no suporti una o més extensions necessàries d'OpenGL. Si us plau, asseguris de tenir els últims controladors de la tarjeta gràfica.<br><br>GL Renderer:<br>%1<br><br>Extensions no suportades:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation>Aquesta compilació no té suport per a OpenGL.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5963,53 +5963,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL není k dispozici!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Chyba při inicializaci OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Vaše grafická karta pravděpodobně nepodporuje OpenGL 4.6 nebo nejsou nainstalovány nejnovější ovladače.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer:<br>%1<br><br>Nepodporované rozšíření:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5971,53 +5971,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5986,53 +5986,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL nicht verfügbar!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Fehler beim Initialisieren von OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Deine Grafikkarte unterstützt OpenGL 4.6 nicht, oder du benutzt nicht die neuste Treiberversion.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer:<br>%1<br><br>Nicht unterstützte Erweiterungen:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5962,53 +5962,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>Το OpenGL δεν είναι διαθέσιμο!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Σφάλμα κατα την αρχικοποίηση του OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6053,53 +6053,47 @@ Por favor, vaya a Configuración -> Sistema -> Red y selecciona una interf
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>¡OpenGL no está disponible!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<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="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>¡Error al inicializar OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Tu GPU no soporta OpenGL 4.6, o no tienes instalado el último controlador de la tarjeta gráfica.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer:<br>%1<br><br>Extensiones no soportadas:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation>Esta compilación no tiene soporte para OpenGL.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5940,53 +5940,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>openGL ei ole saatavilla!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Virhe käynnistäessä OpenGL ydintä!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6029,53 +6029,47 @@ Veuillez aller dans Configurer -> Système -> Réseau puis en choisir une.
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL n'est pas disponible !</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation>Eden n'a pas été compilé avec le support OpenGL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Erreur lors de l'initialisation d'OpenGL !</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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'avez pas les derniers pilotes graphiques.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>Erreur lors de l'initialisation d'OpenGL 4.6 !</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%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="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer :<br>%1<br><br>Extensions non prises en charge :<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5955,53 +5955,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL nem elérhető!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<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="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%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.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer:<br>%1<br><br>Nem támogatott kiterjesztések:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5991,53 +5991,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL tidak tersedia!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Terjadi kesalahan menginisialisasi OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>VGA anda mungkin tidak mendukung OpenGL 4.6, atau anda tidak memiliki pemacu piranti (driver) grafis terbaharu.<br><br>Pemuat GL:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>VGA anda mungkin tidak mendukung satu atau lebih ekstensi OpenGL. Mohon pastikan bahwa anda memiliki pemacu piranti (driver) grafis terbaharu.<br><br>Pemuat GL:<br>%1<br><br>Ekstensi yang tidak didukung:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6052,53 +6052,47 @@ Vai su Configura -> Sistema -> Rete e selezionane una.</translation>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL non disponibile!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<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="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Errore durante l'inizializzazione di OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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'ultima versione dei driver video.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>Errore durante l'inizializzazione di OpenGL 4.6!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>La tua GPU potrebbe non supportare OpenGL 4.6, o non hai installato l'ultima versione dei driver video.<br><br>Renderer GL:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>La tua GPU potrebbe non supportare una o più estensioni OpenGL richieste. Assicurati di aver installato i driver video più recenti.<br><br>Renderer GL:<br>%1<br><br>Estensioni non supportate:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5978,53 +5978,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGLは使用できません!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>OpenGL初期化エラー</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>OpenGL4.6初期化エラー!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>GPUがOpenGL4.6をサポートしていないか、グラフィックスドライバーが最新ではありません。<br><br>GL レンダラ:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>GPUが1つ以上の必要なOpenGL拡張機能をサポートしていない可能性があります。最新のグラフィックドライバを使用していることを確認してください。<br><br>GL レンダラ:<br>%1<br><br>サポートされていない拡張機能:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5976,53 +5976,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL을 사용할 수 없습니다!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation>OpenGL 공유 컨텍스트는 지원되지 않습니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>OpenGL을 초기화하는 동안 오류가 발생했습니다!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>OpenGL 4.6 초기화 중 오류 발생!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>사용하시는 GPU가 OpenGL 4.6을 지원하지 않거나 최신 그래픽 드라이버가 설치되어 있지 않습니다. <br><br>GL 렌더링 장치:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>사용하시는 GPU가 1개 이상의 OpenGL 확장 기능을 지원하지 않습니다. 최신 그래픽 드라이버가 설치되어 있는지 확인하세요. <br><br>GL 렌더링 장치:<br>%1<br><br>지원하지 않는 확장 기능:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5975,53 +5975,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL ikke tilgjengelig!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Feil under initialisering av OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Det kan hende at GPU-en din ikke støtter OpenGL 4.6, eller at du ikke har den nyeste grafikkdriveren.<br><br>GL-renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL-renderer: <br>%1<br><br>Ikke-støttede utvidelser:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5973,53 +5973,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL niet beschikbaar!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Fout tijdens het initialiseren van OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Je GPU ondersteunt mogelijk OpenGL 4.6 niet, of je hebt niet het laatste grafische stuurprogramma.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>Je GPU ondersteunt mogelijk een of meer vereiste OpenGL-extensies niet. Zorg ervoor dat je het laatste grafische stuurprogramma hebt.<br><br>GL Renderer:<br>%1<br><br>Ondersteunde extensies:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6041,53 +6041,47 @@ Przejdź do sekcji Konfiguracja -> System -> Sieć i dokonaj wyboru.</tran
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL niedostępny!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<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="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Błąd podczas inicjowania OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Twoja karta graficzna może nie obsługiwać OpenGL 4.6 lub nie masz najnowszych sterowników karty graficznej.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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<br><br>GL Renderer:<br>%1<br><br>Nieobsługiwane rozszerzenia:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6028,53 +6028,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL não está disponível!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Erro ao inicializar OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%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="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>Renderizador GL:<br>%1<br><br>Extensões não suportadas:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5976,53 +5976,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL não está disponível!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Erro ao inicializar OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%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="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>Renderizador GL:<br>%1<br><br>Extensões não suportadas:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6056,53 +6056,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL не доступен!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation>Общие контексты OpenGL не поддерживаются.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation>Eden не был скомпилирован с поддержкой OpenGL.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Ошибка при инициализации OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>Ошибка при инициализации OpenGL 4.6!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Ваш ГП может не поддерживать OpenGL 4.6, или у вас установлен устаревший графический драйвер.<br><br>Рендерер GL:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>Ваш ГП может не поддерживать одно или несколько требуемых расширений OpenGL. Пожалуйста, убедитесь в том, что у вас установлен последний графический драйвер.<br><br>Рендерер GL:<br>%1<br><br>Неподдерживаемые расширения:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation>В этой сборке отсутствует поддержка OpenGL.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6063,53 +6063,47 @@ Gå till Konfigurera -> System -> Nätverk och gör ett val.</translation>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL är inte tillgängligt!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<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="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Fel vid initiering av OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Din GPU kanske inte stöder OpenGL 4.6, eller så har du inte den senaste grafikdrivrutinen.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer:<br>%1<br><br>Tillägg som inte stöds:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6004,53 +6004,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL kullanıma uygun değil!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<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="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<source>Your GPU may not support OpenGL, or you do not have the latest graphics driver.</source>
|
||||
<translation>GPU'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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>GPU'nuz OpenGL 4.6'yı desteklemiyor veya güncel bir grafik sürücüsüne sahip değilsiniz.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>GPU'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.<br><br>GL Renderer:<br>%1<br><br> Desteklenmeyen Eklentiler:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6058,53 +6058,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL недоступний!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation>Спільні контексти OpenGL не підтримуються.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation>Eden не скомпільовано з підтримкою OpenGL.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>Помилка під час ініціалізації OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>Помилка під час ініціалізації OpenGL 4.6!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>Ваш ГП може не підтримувати OpenGL 4.6 або у вас встановлено застарілий графічний драйвер.<br><br>Візуалізатор GL:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>Ваш ГП може не підтримувати одне або кілька розширень, необхідних для OpenGL. Переконайтеся, що у вас встановлено останній графічний драйвер.<br><br>Візуалізатор GL:<br>%1<br><br>Непідтримувані розширення:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation>Ця збірка не підтримує OpenGL.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5976,53 +5976,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL không khả dụng!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<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="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%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.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer:<br>%1<br><br>Tiện ích không hỗ trợ:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -5976,53 +5976,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>Không có sẵn OpenGL!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<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="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<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="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<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="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%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.<br><br>GL Renderer:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%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.<br><br>GL Renderer:<br>%1<br><br>Tiện ích không hỗ trợ:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6044,53 +6044,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>OpenGL 模式不可用!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation>不支持 OpenGL 共享上下文。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation>Eden 尚未编译为支持 OpenGL。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>初始化 OpenGL 时出错!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>初始化 OpenGL 4.6 时出错!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>您的 GPU 可能不支持 OpenGL 4.6 ,或者您没有安装最新的显卡驱动。<br><br>GL 渲染器:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>您的 GPU 可能不支持某些必需的 OpenGL 扩展。请确保您已经安装最新的显卡驱动。<br><br>GL 渲染器:<br>%1<br><br>不支持的扩展:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
Vendored
+10
-16
@@ -6003,53 +6003,47 @@ Please go to Configure -> System -> Network and make a selection.</source>
|
||||
<context>
|
||||
<name>GRenderWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1012"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1006"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>OpenGL not available!</source>
|
||||
<translation>無法使用 OpenGL 模式!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1013"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1007"/>
|
||||
<source>OpenGL shared contexts are not supported.</source>
|
||||
<translation>不支援 OpenGL 共用的上下文。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1029"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1023"/>
|
||||
<source>Eden has not been compiled with OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1053"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1071"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1082"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1046"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1063"/>
|
||||
<source>Error while initializing OpenGL!</source>
|
||||
<translation>初始化 OpenGL 時發生錯誤!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1054"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1047"/>
|
||||
<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="1063"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1055"/>
|
||||
<source>Error while initializing OpenGL 4.6!</source>
|
||||
<translation>初始化 OpenGL 4.6 時發生錯誤!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1056"/>
|
||||
<source>Your GPU may not support OpenGL 4.6, or you do not have the latest graphics driver.<br><br>GL Renderer:<br>%1</source>
|
||||
<translation>您的 GPU 可能不支援 OpenGL 4.6,或是未安裝最新的圖形驅動程式<br><br>GL 渲染器:<br>%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1072"/>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1064"/>
|
||||
<source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported extensions:<br>%2</source>
|
||||
<translation>您的 GPU 可能不支援某些必需的 OpenGL 功能。請確保您已安裝最新的圖形驅動程式。<br><br>GL 渲染器:<br>%1<br><br>不支援的功能:<br>%2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/yuzu/bootmanager.cpp" line="1083"/>
|
||||
<source>This build doesn't have OpenGL support.</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
|
||||
+2
-2
@@ -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
@@ -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.
|
||||
|
||||
Vendored
+4
-7
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
@@ -469,8 +466,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 {
|
||||
@@ -561,8 +558,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);
|
||||
|
||||
Vendored
+4
-7
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
@@ -29,10 +26,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 {
|
||||
@@ -54,7 +51,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
@@ -185,7 +185,7 @@ else()
|
||||
add_compile_definitions(_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
if (YUZU_STATIC_BUILD AND NOT APPLE AND NOT MSVC)
|
||||
if (YUZU_STATIC_BUILD AND NOT APPLE)
|
||||
add_compile_options(-static)
|
||||
|
||||
# yuzu-cmd requires us to explicitly link libpthread, libgcc, and libstdc++ as static
|
||||
|
||||
@@ -8,8 +8,6 @@ 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
|
||||
@@ -26,25 +24,13 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
|
||||
inner class AddonViewHolder(val binding: ListItemAddonBinding) :
|
||||
AbstractViewHolder<Patch>(binding) {
|
||||
override fun bind(model: Patch) {
|
||||
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.addonCard.setOnClickListener {
|
||||
binding.addonSwitch.performClick()
|
||||
}
|
||||
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)
|
||||
@@ -53,9 +39,12 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
|
||||
model.enabled = checked
|
||||
}
|
||||
}
|
||||
val canDelete = model.isRemovable && !isCheat
|
||||
binding.deleteCard.isVisible = canDelete
|
||||
binding.buttonDelete.isVisible = canDelete
|
||||
|
||||
val canDelete = model.isRemovable
|
||||
binding.deleteCard.isEnabled = canDelete
|
||||
binding.buttonDelete.isEnabled = canDelete
|
||||
binding.deleteCard.alpha = if (canDelete) 1f else 0.38f
|
||||
|
||||
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.toList())
|
||||
(binding.listAddons.adapter as AddonAdapter).submitList(it)
|
||||
}
|
||||
addonViewModel.showModInstallPicker.collect(
|
||||
viewLifecycleOwner,
|
||||
|
||||
@@ -31,17 +31,12 @@ 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)
|
||||
}
|
||||
@@ -71,7 +66,8 @@ class AddonViewModel : ViewModel() {
|
||||
NativeLibrary.getPatchesForFile(currentGame.path, currentGame.programId)
|
||||
} ?: return@launch
|
||||
|
||||
val patchList = sortPatchesWithCheatsGrouped(patches.toMutableList())
|
||||
val patchList = patches.toMutableList()
|
||||
patchList.sortBy { it.name }
|
||||
|
||||
// Ensure only one update is enabled
|
||||
ensureSingleUpdateEnabled(patchList)
|
||||
@@ -150,7 +146,6 @@ 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)
|
||||
}
|
||||
@@ -184,7 +179,7 @@ class AddonViewModel : ViewModel() {
|
||||
it.name
|
||||
}
|
||||
} else {
|
||||
it.getStorageKey()
|
||||
it.name
|
||||
}
|
||||
}
|
||||
}.toTypedArray()
|
||||
@@ -206,28 +201,4 @@ 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,9 +17,7 @@ data class Patch(
|
||||
val programId: String,
|
||||
val titleId: String,
|
||||
val numericVersion: Long = 0,
|
||||
val source: Int = 0,
|
||||
val parentName: String = "", // For cheats: name of the mod folder containing them
|
||||
val cheatCompat: Int = CHEAT_COMPAT_COMPATIBLE
|
||||
val source: Int = 0
|
||||
) {
|
||||
companion object {
|
||||
const val SOURCE_UNKNOWN = 0
|
||||
@@ -27,31 +25,8 @@ 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,6 +1,3 @@
|
||||
// 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
|
||||
|
||||
@@ -9,8 +6,7 @@ package org.yuzu.yuzu_emu.model
|
||||
enum class PatchType(val int: Int) {
|
||||
Update(0),
|
||||
DLC(1),
|
||||
Mod(2),
|
||||
Cheat(3);
|
||||
Mod(2);
|
||||
|
||||
companion object {
|
||||
fun from(int: Int): PatchType = entries.firstOrNull { it.int == int } ?: Update
|
||||
|
||||
@@ -1406,9 +1406,7 @@ 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),
|
||||
Common::Android::ToJString(env, patch.parent_name),
|
||||
static_cast<jint>(patch.cheat_compat));
|
||||
static_cast<jlong>(patch.numeric_version), static_cast<jint>(patch.source));
|
||||
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">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
||||
<string name="rescale_hack_description">Fixes Luigi Mansion 3 artifact lines.</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>
|
||||
|
||||
@@ -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;JILjava/lang/String;)V");
|
||||
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)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;");
|
||||
|
||||
@@ -554,13 +554,8 @@ struct Values {
|
||||
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<bool> rescale_hack{linkage,
|
||||
#ifdef __ANDROID__
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
"rescale_hack", Category::RendererHacks};
|
||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
|
||||
+5
-12
@@ -616,18 +616,6 @@ 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
|
||||
@@ -1165,6 +1153,11 @@ 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
|
||||
|
||||
@@ -761,7 +761,8 @@ 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);
|
||||
@@ -771,9 +772,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, bool>(visitor, instruction);
|
||||
auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction);
|
||||
if (decoder) {
|
||||
was_executed = *decoder;
|
||||
was_executed = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
was_executed = false;
|
||||
}
|
||||
|
||||
+11
-137
@@ -40,7 +40,6 @@
|
||||
#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"
|
||||
@@ -55,6 +54,7 @@
|
||||
#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,17 +278,8 @@ 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;
|
||||
@@ -349,6 +340,11 @@ 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);
|
||||
@@ -409,6 +405,7 @@ struct System::Impl {
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
fs_controller.Reset();
|
||||
cheat_engine.reset();
|
||||
core_timing.ClearPendingEvents();
|
||||
app_loader.reset();
|
||||
audio_core.reset();
|
||||
@@ -478,6 +475,7 @@ 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;
|
||||
|
||||
@@ -497,17 +495,6 @@ 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{};
|
||||
@@ -525,61 +512,6 @@ 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)} {}
|
||||
@@ -817,61 +749,11 @@ FileSys::VirtualFilesystem System::GetFilesystem() const {
|
||||
return impl->virtual_filesystem;
|
||||
}
|
||||
|
||||
void System::RegisterCheatList(const std::vector<Service::DMNT::CheatEntry>& list,
|
||||
void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
||||
const std::array<u8, 32>& build_id, u64 main_region_begin,
|
||||
u64 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);
|
||||
}
|
||||
}
|
||||
impl->cheat_engine.emplace(*this, list, build_id);
|
||||
impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
|
||||
}
|
||||
|
||||
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
|
||||
@@ -1011,14 +893,6 @@ 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));
|
||||
}
|
||||
|
||||
+2
-10
@@ -17,7 +17,6 @@
|
||||
|
||||
#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"
|
||||
|
||||
@@ -46,14 +45,10 @@ 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 {
|
||||
@@ -340,7 +335,7 @@ public:
|
||||
|
||||
[[nodiscard]] FileSys::VirtualFilesystem GetFilesystem() const;
|
||||
|
||||
void RegisterCheatList(const std::vector<Service::DMNT::CheatEntry>& list,
|
||||
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
|
||||
const std::array<u8, 0x20>& build_id, u64 main_region_begin,
|
||||
u64 main_region_size);
|
||||
|
||||
@@ -381,9 +376,6 @@ public:
|
||||
|
||||
[[nodiscard]] Tools::RenderdocAPI& GetRenderdocAPI();
|
||||
|
||||
[[nodiscard]] Service::DMNT::CheatProcessManager& GetCheatManager();
|
||||
[[nodiscard]] const Service::DMNT::CheatProcessManager& GetCheatManager() const;
|
||||
|
||||
void SetExitLocked(bool locked);
|
||||
bool GetExitLocked() const;
|
||||
|
||||
|
||||
+284
-324
@@ -27,13 +27,12 @@
|
||||
#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 {
|
||||
@@ -65,15 +64,16 @@ std::string FormatTitleVersion(u32 version,
|
||||
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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) {
|
||||
#ifdef _WIN32
|
||||
return dir->GetSubdirectory(name);
|
||||
#else
|
||||
const auto target = Common::ToLower(std::string(name));
|
||||
for (const auto& subdir : dir->GetSubdirectories()) {
|
||||
if (Common::ToLower(subdir->GetName()) == target) {
|
||||
const auto subdirs = dir->GetSubdirectories();
|
||||
for (const auto& subdir : subdirs) {
|
||||
std::string dir_name = Common::ToLower(subdir->GetName());
|
||||
if (dir_name == name) {
|
||||
return subdir;
|
||||
}
|
||||
}
|
||||
@@ -82,36 +82,36 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir& dir, std::string_view name
|
||||
#endif
|
||||
}
|
||||
|
||||
std::optional<std::vector<Service::DMNT::CheatEntry>> ReadCheatFileFromFolder(
|
||||
std::optional<std::vector<Core::Memory::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, std::min(build_id_raw.size(), sizeof(u64) * 2));
|
||||
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
|
||||
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
|
||||
|
||||
if (file == nullptr) {
|
||||
LOG_DEBUG(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
|
||||
title_id, build_id);
|
||||
LOG_INFO(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_WARNING(Common_Filesystem,
|
||||
"Failed to read cheats file for title_id={:016X}, build_id={}", title_id,
|
||||
build_id);
|
||||
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
|
||||
title_id, build_id);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const Service::DMNT::CheatParser parser;
|
||||
const Core::Memory::TextCheatParser 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()) {
|
||||
if (to.empty()) {
|
||||
to += with;
|
||||
} else {
|
||||
to += ", ";
|
||||
to += with;
|
||||
}
|
||||
to += with;
|
||||
}
|
||||
|
||||
bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
|
||||
@@ -130,7 +130,8 @@ 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 {};
|
||||
}
|
||||
|
||||
@@ -168,11 +169,16 @@ u64 PatchManager::GetTitleID() const {
|
||||
return title_id;
|
||||
}
|
||||
|
||||
PatchManager::UpdateResolution PatchManager::GetActiveUpdate(ContentRecordType type) const {
|
||||
UpdateResolution 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& disabled = Settings::values.disabled_addons[title_id];
|
||||
|
||||
bool update_disabled = true;
|
||||
std::optional<u32> enabled_version;
|
||||
bool checked_external = false;
|
||||
bool checked_manual = false;
|
||||
|
||||
@@ -189,10 +195,8 @@ PatchManager::UpdateResolution PatchManager::GetActiveUpdate(ContentRecordType t
|
||||
checked_external = true;
|
||||
for (const auto& update_entry : update_versions) {
|
||||
if (!IsVersionedExternalUpdateDisabled(disabled, 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);
|
||||
update_disabled = false;
|
||||
enabled_version = update_entry.version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -210,10 +214,8 @@ PatchManager::UpdateResolution PatchManager::GetActiveUpdate(ContentRecordType t
|
||||
checked_manual = true;
|
||||
for (const auto& update_entry : manual_update_versions) {
|
||||
if (!IsVersionedExternalUpdateDisabled(disabled, 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);
|
||||
update_disabled = false;
|
||||
enabled_version = update_entry.version;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -225,41 +227,37 @@ PatchManager::UpdateResolution PatchManager::GetActiveUpdate(ContentRecordType t
|
||||
// check for original NAND style
|
||||
// Check NAND if: no external updates exist, OR all external updates are disabled
|
||||
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();
|
||||
// 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);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
} 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();
|
||||
} 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);
|
||||
std::nullopt, TitleType::Update, ContentRecordType::Program, update_tid);
|
||||
|
||||
for (const auto& [slot, entry] : nand_sdmc_entries) {
|
||||
if (slot == ContentProviderUnionSlot::UserNAND ||
|
||||
slot == ContentProviderUnionSlot::SysNAND) {
|
||||
if (!nand_disabled) {
|
||||
update_res.update_active_raw =
|
||||
content_provider.GetEntryRaw(update_tid, type);
|
||||
update_res.update_disabled = update_res.update_active_raw == nullptr;
|
||||
update_disabled = false;
|
||||
break;
|
||||
}
|
||||
} else if (slot == ContentProviderUnionSlot::SDMC) {
|
||||
if (!sdmc_disabled) {
|
||||
update_res.update_active_raw =
|
||||
content_provider.GetEntryRaw(update_tid, type);
|
||||
update_res.update_disabled = update_res.update_active_raw == nullptr;
|
||||
update_disabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -267,36 +265,44 @@ PatchManager::UpdateResolution PatchManager::GetActiveUpdate(ContentRecordType t
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Game Updates
|
||||
std::unique_ptr<NCA> update = nullptr;
|
||||
|
||||
if (update_res.update_active_version.has_value() && update_res.update_active_raw != nullptr) {
|
||||
update = std::make_unique<NCA>(update_res.update_active_raw);
|
||||
// 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 == nullptr && !update_res.update_disabled) {
|
||||
// Fallback to regular content provider if no external update was loaded
|
||||
if (update == nullptr && !update_disabled) {
|
||||
update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
|
||||
}
|
||||
|
||||
if (!update_res.update_disabled && update != nullptr && update->GetExeFS() != nullptr) {
|
||||
if (!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);
|
||||
|
||||
@@ -462,8 +468,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
||||
return !CollectPatches(patch_dirs, build_id).empty();
|
||||
}
|
||||
|
||||
std::vector<Service::DMNT::CheatEntry> PatchManager::CreateCheatList(
|
||||
const BuildID& build_id_) const {
|
||||
std::vector<Core::Memory::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);
|
||||
@@ -472,72 +477,35 @@ std::vector<Service::DMNT::CheatEntry> PatchManager::CreateCheatList(
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
// Load cheats from: <mod dir>/<folder>/cheats/<build_id>.txt
|
||||
std::vector<Service::DMNT::CheatEntry> out;
|
||||
// <mod dir> / <folder> / cheats / <build id>.txt
|
||||
std::vector<Core::Memory::CheatEntry> out;
|
||||
for (const auto& subdir : patch_dirs) {
|
||||
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);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -629,22 +597,105 @@ 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 update_res = GetActiveUpdate(type);
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
|
||||
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);
|
||||
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 (new_nca->GetStatus() == Loader::ResultStatus::Success &&
|
||||
new_nca->GetRomFS() != nullptr) {
|
||||
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)));
|
||||
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
|
||||
enabled_version.has_value() ? FormatTitleVersion(*enabled_version) :
|
||||
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
|
||||
romfs = new_nca->GetRomFS();
|
||||
}
|
||||
} else if (!update_res.update_disabled && packed_update_raw != nullptr && base_nca != nullptr) {
|
||||
} else if (!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) {
|
||||
@@ -661,53 +712,6 @@ 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 {};
|
||||
@@ -745,8 +749,7 @@ 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,
|
||||
.parent_name = ""};
|
||||
.numeric_version = update_entry.version};
|
||||
|
||||
external_update_patches.push_back(update_patch);
|
||||
}
|
||||
@@ -766,6 +769,7 @@ 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,
|
||||
@@ -810,17 +814,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;
|
||||
@@ -891,137 +895,94 @@ 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);
|
||||
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(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(),
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
for (const auto& mod : mod_dir->GetSubdirectories()) {
|
||||
std::string types;
|
||||
bool has_cheats = false;
|
||||
|
||||
const auto exefs_dir = FindSubdirectoryCaseless(mod, "exefs");
|
||||
if (IsDirValidAndNonEmpty(exefs_dir)) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
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)
|
||||
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
||||
}
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfs")) ||
|
||||
IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "romfslite")))
|
||||
AppendCommaIfNotEmpty(types, "LayeredFS");
|
||||
|
||||
const auto cheats_dir = FindSubdirectoryCaseless(mod, "cheats");
|
||||
if (IsDirValidAndNonEmpty(cheats_dir)) {
|
||||
has_cheats = true;
|
||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(mod, "cheats")))
|
||||
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_name) != disabled.end();
|
||||
const auto mod_disabled = std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end();
|
||||
out.push_back({.enabled = !mod_disabled,
|
||||
.name = mod_name,
|
||||
.name = mod->GetName(),
|
||||
.version = types,
|
||||
.type = PatchType::Mod,
|
||||
.program_id = title_id,
|
||||
.title_id = title_id,
|
||||
.source = PatchSource::Unknown,
|
||||
.location = mod->GetFullPath(),
|
||||
.parent_name = ""});
|
||||
.location = mod->GetFullPath()});
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
// 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");
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,8 +992,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) {
|
||||
@@ -1063,20 +1024,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);
|
||||
}
|
||||
@@ -1108,8 +1069,7 @@ 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,
|
||||
.parent_name = ""});
|
||||
.source = dlc_source});
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
@@ -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,17 +23,13 @@ namespace Service::FileSystem {
|
||||
class FileSystemController;
|
||||
}
|
||||
|
||||
namespace Service::DMNT {
|
||||
struct CheatEntry;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class ContentProvider;
|
||||
class NCA;
|
||||
class NACP;
|
||||
|
||||
enum class PatchType { Update, DLC, Mod, Cheat };
|
||||
enum class PatchType { Update, DLC, Mod };
|
||||
|
||||
enum class PatchSource {
|
||||
Unknown,
|
||||
@@ -43,11 +39,6 @@ enum class PatchSource {
|
||||
Packed,
|
||||
};
|
||||
|
||||
enum class CheatCompatibility : u8 {
|
||||
Incompatible,
|
||||
Compatible,
|
||||
};
|
||||
|
||||
struct Patch {
|
||||
bool enabled;
|
||||
std::string name;
|
||||
@@ -58,8 +49,6 @@ 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.
|
||||
@@ -90,7 +79,7 @@ public:
|
||||
[[nodiscard]] bool HasNSOPatch(const BuildID& build_id, std::string_view name) const;
|
||||
|
||||
// Creates a CheatList object with all
|
||||
[[nodiscard]] std::vector<Service::DMNT::CheatEntry> CreateCheatList(
|
||||
[[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
|
||||
const BuildID& build_id) const;
|
||||
|
||||
// Currently tracked RomFS patches:
|
||||
@@ -101,11 +90,9 @@ public:
|
||||
VirtualFile packed_update_raw = nullptr,
|
||||
bool apply_layeredfs = true) const;
|
||||
|
||||
// Returns a vector of patches including individual cheats
|
||||
// Returns a vector of patches
|
||||
[[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
|
||||
@@ -122,14 +109,6 @@ 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;
|
||||
|
||||
@@ -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(ThreadLocalData& t, KThread* existing_thread) {
|
||||
if (t.thread == nullptr) {
|
||||
KThread* GetHostDummyThread(KThread* existing_thread) {
|
||||
if (tls_data.thread == nullptr) {
|
||||
auto const initialize{[](KThread* thread) {
|
||||
ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess());
|
||||
return thread;
|
||||
}};
|
||||
t.raw_thread.emplace(system.Kernel());
|
||||
t.thread = existing_thread ? existing_thread : initialize(&*t.raw_thread);
|
||||
ASSERT(t.thread != nullptr);
|
||||
tls_data.raw_thread.emplace(system.Kernel());
|
||||
tls_data.thread = existing_thread ? existing_thread : initialize(&*tls_data.raw_thread);
|
||||
ASSERT(tls_data.thread != nullptr);
|
||||
}
|
||||
return t.thread;
|
||||
return tls_data.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(tls_data, existing_thread);
|
||||
(void)GetHostDummyThread(existing_thread);
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetCurrentHostThreadID() {
|
||||
@@ -419,8 +419,9 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
KThread* GetCurrentEmuThread() {
|
||||
auto& t = tls_data;
|
||||
return t.current_thread ? t.current_thread : (t.current_thread = GetHostDummyThread(t, nullptr));
|
||||
if (!tls_data.current_thread)
|
||||
tls_data.current_thread = GetHostDummyThread(nullptr);
|
||||
return tls_data.current_thread;
|
||||
}
|
||||
|
||||
void SetCurrentEmuThread(KThread* thread) {
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
// 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
|
||||
@@ -1,88 +0,0 @@
|
||||
// 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
|
||||
@@ -1,121 +0,0 @@
|
||||
// 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
|
||||
@@ -1,26 +0,0 @@
|
||||
// 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
|
||||
@@ -1,599 +0,0 @@
|
||||
// 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
|
||||
@@ -1,126 +0,0 @@
|
||||
// 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,26 +0,0 @@
|
||||
// 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
|
||||
@@ -1,15 +0,0 @@
|
||||
// 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
|
||||
@@ -1,26 +0,0 @@
|
||||
// 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
|
||||
@@ -1,55 +0,0 @@
|
||||
// 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
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -16,7 +16,6 @@
|
||||
#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"
|
||||
@@ -108,7 +107,6 @@ 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},
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
// 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
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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
|
||||
+153
-154
@@ -1,223 +1,226 @@
|
||||
// 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/hle/service/dmnt/cheat_process_manager.h"
|
||||
#include "core/hle/service/dmnt/cheat_virtual_machine.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
#include "core/memory/dmnt_cheat_vm.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
CheatVirtualMachine::CheatVirtualMachine(CheatProcessManager& cheat_manager)
|
||||
: manager(cheat_manager) {}
|
||||
namespace Core::Memory {
|
||||
|
||||
CheatVirtualMachine::~CheatVirtualMachine() = default;
|
||||
DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks_)
|
||||
: callbacks(std::move(callbacks_)) {}
|
||||
|
||||
void CheatVirtualMachine::DebugLog(u32 log_id, u64 value) const {
|
||||
manager.DebugLog(static_cast<u8>(log_id), value);
|
||||
DmntCheatVm::~DmntCheatVm() = default;
|
||||
|
||||
void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
|
||||
callbacks->DebugLog(static_cast<u8>(log_id), value);
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::LogOpcode(const CheatVmOpcode& opcode) const {
|
||||
void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
|
||||
if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Store Static");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Store Static");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type)));
|
||||
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));
|
||||
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));
|
||||
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) {
|
||||
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));
|
||||
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));
|
||||
} else if (std::holds_alternative<EndConditionalOpcode>(opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: End Conditional");
|
||||
callbacks->CommandLog("Opcode: End Conditional");
|
||||
} else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) {
|
||||
if (ctrl_loop->start_loop) {
|
||||
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));
|
||||
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));
|
||||
} else {
|
||||
manager.CommandLog("Opcode: End Loop");
|
||||
manager.CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
|
||||
callbacks->CommandLog("Opcode: End Loop");
|
||||
callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
|
||||
}
|
||||
} else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) {
|
||||
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));
|
||||
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));
|
||||
} else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) {
|
||||
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));
|
||||
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));
|
||||
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) {
|
||||
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));
|
||||
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));
|
||||
if (str_static->add_offset_reg) {
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
|
||||
}
|
||||
manager.CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
|
||||
manager.CommandLog(fmt::format("Value: {:X}", str_static->value));
|
||||
callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value));
|
||||
} else if (auto perform_math_static =
|
||||
std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) {
|
||||
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(
|
||||
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(
|
||||
fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type)));
|
||||
manager.CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
|
||||
} else if (auto begin_keypress_cond =
|
||||
std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Begin Keypress Conditional");
|
||||
manager.CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
|
||||
callbacks->CommandLog("Opcode: Begin Keypress Conditional");
|
||||
callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask));
|
||||
} else if (auto perform_math_reg =
|
||||
std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) {
|
||||
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));
|
||||
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));
|
||||
if (perform_math_reg->has_immediate) {
|
||||
manager.CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64));
|
||||
} else {
|
||||
manager.CommandLog(fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
|
||||
}
|
||||
} else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) {
|
||||
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));
|
||||
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));
|
||||
switch (str_register->ofs_type) {
|
||||
case StoreRegisterOffsetType::None:
|
||||
break;
|
||||
case StoreRegisterOffsetType::Reg:
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
|
||||
break;
|
||||
case StoreRegisterOffsetType::Imm:
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
break;
|
||||
case StoreRegisterOffsetType::MemReg:
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
|
||||
break;
|
||||
case StoreRegisterOffsetType::MemImm:
|
||||
case StoreRegisterOffsetType::MemImmReg:
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type)));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
|
||||
break;
|
||||
}
|
||||
} else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) {
|
||||
manager.CommandLog("Opcode: Begin Register Conditional");
|
||||
manager.CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Begin Register Conditional");
|
||||
callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type)));
|
||||
manager.CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
|
||||
callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
|
||||
switch (begin_reg_cond->comp_type) {
|
||||
case CompareRegisterValueType::StaticValue:
|
||||
manager.CommandLog("Comp Type: Static Value");
|
||||
manager.CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
|
||||
callbacks->CommandLog("Comp Type: Static Value");
|
||||
callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64));
|
||||
break;
|
||||
case CompareRegisterValueType::OtherRegister:
|
||||
manager.CommandLog("Comp Type: Other Register");
|
||||
manager.CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
|
||||
callbacks->CommandLog("Comp Type: Other Register");
|
||||
callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
|
||||
break;
|
||||
case CompareRegisterValueType::MemoryRelAddr:
|
||||
manager.CommandLog("Comp Type: Memory Relative Address");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Comp Type: Memory Relative Address");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
|
||||
break;
|
||||
case CompareRegisterValueType::MemoryOfsReg:
|
||||
manager.CommandLog("Comp Type: Memory Offset Register");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Comp Type: Memory Offset Register");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
|
||||
break;
|
||||
case CompareRegisterValueType::RegisterRelAddr:
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
case CompareRegisterValueType::RegisterOfsReg:
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
}
|
||||
} else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) {
|
||||
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(
|
||||
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(
|
||||
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)) {
|
||||
manager.CommandLog("Opcode: Save or Restore Register Mask");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Opcode: Save or Restore Register Mask");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type)));
|
||||
for (std::size_t i = 0; i < NumRegisters; i++) {
|
||||
manager.CommandLog(
|
||||
callbacks->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)) {
|
||||
manager.CommandLog("Opcode: Read/Write Static Register");
|
||||
callbacks->CommandLog("Opcode: Read/Write Static Register");
|
||||
if (rw_static_reg->static_idx < NumReadableStaticRegisters) {
|
||||
manager.CommandLog("Op Type: ReadStaticRegister");
|
||||
callbacks->CommandLog("Op Type: ReadStaticRegister");
|
||||
} else {
|
||||
manager.CommandLog("Op Type: WriteStaticRegister");
|
||||
callbacks->CommandLog("Op Type: WriteStaticRegister");
|
||||
}
|
||||
manager.CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
|
||||
manager.CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
|
||||
callbacks->CommandLog(fmt::format("Reg Idx {:X}", rw_static_reg->idx));
|
||||
callbacks->CommandLog(fmt::format("Stc Idx {:X}", rw_static_reg->static_idx));
|
||||
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
|
||||
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)));
|
||||
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)));
|
||||
switch (debug_log->val_type) {
|
||||
case DebugLogValueType::RegisterValue:
|
||||
manager.CommandLog("Val Type: Register Value");
|
||||
manager.CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
|
||||
callbacks->CommandLog("Val Type: Register Value");
|
||||
callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
|
||||
break;
|
||||
case DebugLogValueType::MemoryRelAddr:
|
||||
manager.CommandLog("Val Type: Memory Relative Address");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Val Type: Memory Relative Address");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
|
||||
manager.CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
|
||||
callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
|
||||
break;
|
||||
case DebugLogValueType::MemoryOfsReg:
|
||||
manager.CommandLog("Val Type: Memory Offset Register");
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog("Val Type: Memory Offset Register");
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type)));
|
||||
manager.CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
|
||||
callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
|
||||
break;
|
||||
case DebugLogValueType::RegisterRelAddr:
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
case DebugLogValueType::RegisterOfsReg:
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
}
|
||||
} else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) {
|
||||
manager.CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
|
||||
callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
|
||||
}
|
||||
}
|
||||
|
||||
bool CheatVirtualMachine::DecodeNextOpcode(CheatVmOpcode& out) {
|
||||
DmntCheatVm::Callbacks::~Callbacks() = default;
|
||||
|
||||
bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
|
||||
// If we've ever seen a decode failure, return false.
|
||||
bool valid = decode_success;
|
||||
CheatVmOpcode opcode = {};
|
||||
@@ -631,7 +634,7 @@ bool CheatVirtualMachine::DecodeNextOpcode(CheatVmOpcode& out) {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::SkipConditionalBlock(bool is_if) {
|
||||
void DmntCheatVm::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;
|
||||
@@ -665,7 +668,7 @@ void CheatVirtualMachine::SkipConditionalBlock(bool is_if) {
|
||||
}
|
||||
}
|
||||
|
||||
u64 CheatVirtualMachine::GetVmInt(VmInt value, u32 bit_width) {
|
||||
u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
|
||||
switch (bit_width) {
|
||||
case 1:
|
||||
return value.bit8;
|
||||
@@ -681,8 +684,8 @@ u64 CheatVirtualMachine::GetVmInt(VmInt value, u32 bit_width) {
|
||||
}
|
||||
}
|
||||
|
||||
u64 CheatVirtualMachine::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
|
||||
MemoryAccessType mem_type, u64 rel_address) {
|
||||
u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
|
||||
MemoryAccessType mem_type, u64 rel_address) {
|
||||
switch (mem_type) {
|
||||
case MemoryAccessType::MainNso:
|
||||
default:
|
||||
@@ -696,7 +699,7 @@ u64 CheatVirtualMachine::GetCheatProcessAddress(const CheatProcessMetadata& meta
|
||||
}
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::ResetState() {
|
||||
void DmntCheatVm::ResetState() {
|
||||
registers.fill(0);
|
||||
saved_values.fill(0);
|
||||
loop_tops.fill(0);
|
||||
@@ -705,7 +708,7 @@ void CheatVirtualMachine::ResetState() {
|
||||
decode_success = true;
|
||||
}
|
||||
|
||||
bool CheatVirtualMachine::LoadProgram(std::span<const CheatEntry> entries) {
|
||||
bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
|
||||
// Reset opcode count.
|
||||
num_opcodes = 0;
|
||||
|
||||
@@ -726,31 +729,31 @@ bool CheatVirtualMachine::LoadProgram(std::span<const CheatEntry> entries) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
||||
CheatVmOpcode cur_opcode{};
|
||||
|
||||
// Get Keys down.
|
||||
u64 kDown = manager.HidKeysDown();
|
||||
u64 kDown = callbacks->HidKeysDown();
|
||||
|
||||
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)));
|
||||
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)));
|
||||
|
||||
// Clear VM state.
|
||||
ResetState();
|
||||
|
||||
// Loop until program finishes.
|
||||
while (DecodeNextOpcode(cur_opcode)) {
|
||||
manager.CommandLog(
|
||||
callbacks->CommandLog(
|
||||
fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr)));
|
||||
|
||||
for (std::size_t i = 0; i < NumRegisters; i++) {
|
||||
manager.CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
|
||||
callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < NumRegisters; i++) {
|
||||
manager.CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
|
||||
callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
|
||||
}
|
||||
LogOpcode(cur_opcode);
|
||||
|
||||
@@ -770,8 +773,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
|
||||
store_static->bit_width);
|
||||
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
|
||||
break;
|
||||
}
|
||||
} else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
|
||||
@@ -784,8 +786,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(src_address, &src_value,
|
||||
begin_cond->bit_width);
|
||||
callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
|
||||
break;
|
||||
}
|
||||
// Check against condition.
|
||||
@@ -856,8 +857,8 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(src_address, ®isters[ldr_memory->reg_index],
|
||||
ldr_memory->bit_width);
|
||||
callbacks->MemoryReadUnsafe(src_address, ®isters[ldr_memory->reg_index],
|
||||
ldr_memory->bit_width);
|
||||
break;
|
||||
}
|
||||
} else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
|
||||
@@ -873,8 +874,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
|
||||
str_static->bit_width);
|
||||
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
|
||||
break;
|
||||
}
|
||||
// Increment register if relevant.
|
||||
@@ -1032,8 +1032,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.WriteCheatProcessMemoryUnsafe(dst_address, &dst_value,
|
||||
str_register->bit_width);
|
||||
callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1112,8 +1111,8 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(cond_address, &cond_value,
|
||||
begin_reg_cond->bit_width);
|
||||
callbacks->MemoryReadUnsafe(cond_address, &cond_value,
|
||||
begin_reg_cond->bit_width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1206,9 +1205,9 @@ void CheatVirtualMachine::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)) {
|
||||
manager.PauseCheatProcessUnsafe();
|
||||
callbacks->PauseProcess();
|
||||
} else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
|
||||
manager.ResumeCheatProcessUnsafe();
|
||||
callbacks->ResumeProcess();
|
||||
} else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
|
||||
// Read value from memory.
|
||||
u64 log_value = 0;
|
||||
@@ -1255,8 +1254,7 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
manager.ReadCheatProcessMemoryUnsafe(val_address, &log_value,
|
||||
debug_log->bit_width);
|
||||
callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1266,4 +1264,5 @@ void CheatVirtualMachine::Execute(const CheatProcessMetadata& metadata) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Service::DMNT
|
||||
|
||||
} // namespace Core::Memory
|
||||
+43
-36
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -6,14 +6,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <fmt/printf.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/dmnt/dmnt_types.h"
|
||||
#include "core/memory/dmnt_cheat_types.h"
|
||||
|
||||
namespace Service::DMNT {
|
||||
class CheatProcessManager;
|
||||
namespace Core::Memory {
|
||||
|
||||
enum class CheatVmOpcodeType : u32 {
|
||||
StoreStatic = 0,
|
||||
@@ -263,8 +264,25 @@ struct CheatVmOpcode {
|
||||
opcode{};
|
||||
};
|
||||
|
||||
class CheatVirtualMachine {
|
||||
class DmntCheatVm {
|
||||
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;
|
||||
@@ -272,43 +290,18 @@ public:
|
||||
static constexpr std::size_t NumStaticRegisters =
|
||||
NumReadableStaticRegisters + NumWritableStaticRegisters;
|
||||
|
||||
explicit CheatVirtualMachine(CheatProcessManager& cheat_manager);
|
||||
~CheatVirtualMachine();
|
||||
explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks_);
|
||||
~DmntCheatVm();
|
||||
|
||||
std::size_t GetProgramSize() const {
|
||||
return this->num_opcodes;
|
||||
}
|
||||
|
||||
bool LoadProgram(std::span<const CheatEntry> cheats);
|
||||
bool LoadProgram(const std::vector<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:
|
||||
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::unique_ptr<Callbacks> callbacks;
|
||||
|
||||
std::size_t num_opcodes = 0;
|
||||
std::size_t instruction_ptr = 0;
|
||||
@@ -319,5 +312,19 @@ 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 Service::DMNT
|
||||
|
||||
}; // namespace Core::Memory
|
||||
@@ -26,41 +26,48 @@ namespace Dynarmic::A32 {
|
||||
template<typename Visitor>
|
||||
using ArmMatcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
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);
|
||||
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)),
|
||||
#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 t;
|
||||
}();
|
||||
for (auto const& e : table[make_fast_index(instruction)])
|
||||
if ((instruction & e.mask) == e.expect)
|
||||
return e.fn(visitor, instruction);
|
||||
return std::nullopt;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
static std::optional<std::string_view> GetNameArm(u32 inst) noexcept {
|
||||
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 {
|
||||
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,16 +26,17 @@ namespace Dynarmic::A32 {
|
||||
template<typename Visitor>
|
||||
using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
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);
|
||||
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)),
|
||||
#include "./asimd.inc"
|
||||
#undef INST
|
||||
return std::nullopt;
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
|
||||
@@ -23,16 +23,17 @@ namespace Dynarmic::A32 {
|
||||
template<typename Visitor>
|
||||
using Thumb16Matcher = Decoder::Matcher<Visitor, u16>;
|
||||
|
||||
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);
|
||||
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)),
|
||||
#include "./thumb16.inc"
|
||||
#undef INST
|
||||
return std::nullopt;
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
|
||||
@@ -22,16 +22,17 @@ namespace Dynarmic::A32 {
|
||||
template<typename Visitor>
|
||||
using Thumb32Matcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
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);
|
||||
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)),
|
||||
#include "./thumb32.inc"
|
||||
#undef INST
|
||||
return std::nullopt;
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
|
||||
@@ -23,18 +23,32 @@ namespace Dynarmic::A32 {
|
||||
template<typename Visitor>
|
||||
using VFPMatcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
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);
|
||||
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)),
|
||||
#include "./vfp.inc"
|
||||
#undef INST
|
||||
return std::nullopt;
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
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, 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;
|
||||
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);
|
||||
} 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, 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;
|
||||
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);
|
||||
} 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, bool>(visitor, u16(thumb_instruction))) {
|
||||
should_continue = *decoder;
|
||||
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
||||
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
||||
} else {
|
||||
should_continue = visitor.thumb16_UDF();
|
||||
}
|
||||
} else {
|
||||
if (MaybeVFPOrASIMDInstruction(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;
|
||||
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);
|
||||
} else {
|
||||
should_continue = visitor.thumb32_UDF();
|
||||
}
|
||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
|
||||
should_continue = *decoder;
|
||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||
} 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, bool>(visitor, u16(thumb_instruction))) {
|
||||
should_continue = *decoder;
|
||||
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
||||
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
||||
} 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, 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;
|
||||
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);
|
||||
} else {
|
||||
should_continue = visitor.thumb32_UDF();
|
||||
}
|
||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor, bool>(visitor, thumb_instruction)) {
|
||||
should_continue = *decoder;
|
||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||
} else {
|
||||
should_continue = visitor.thumb32_UDF();
|
||||
}
|
||||
|
||||
@@ -26,37 +26,45 @@ namespace Dynarmic::A64 {
|
||||
template<typename Visitor>
|
||||
using Matcher = Decoder::Matcher<Visitor, u32>;
|
||||
|
||||
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);
|
||||
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)),
|
||||
#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 t;
|
||||
}();
|
||||
for (auto const& e : table[make_fast_index(instruction)])
|
||||
if ((instruction & e.mask) == e.expect)
|
||||
return e.fn(visitor, instruction);
|
||||
return std::nullopt;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
|
||||
@@ -24,8 +24,9 @@ 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)) {
|
||||
if (auto decoder = Decode<TranslatorVisitor, bool>(visitor, *instruction)) {
|
||||
should_continue = *decoder;
|
||||
auto decoder = Decode<TranslatorVisitor>(*instruction);
|
||||
if (decoder) {
|
||||
should_continue = decoder->get().call(visitor, *instruction);
|
||||
} else {
|
||||
should_continue = visitor.RaiseException(Exception::UnallocatedEncoding);
|
||||
}
|
||||
@@ -46,9 +47,13 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu
|
||||
bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction) {
|
||||
TranslatorVisitor visitor{block, descriptor, {}};
|
||||
|
||||
bool should_continue = false;
|
||||
if (auto const decoder = Decode<TranslatorVisitor, bool>(visitor, instruction))
|
||||
should_continue = *decoder;
|
||||
bool should_continue = true;
|
||||
auto const decoder = Decode<TranslatorVisitor>(instruction);
|
||||
if (decoder) {
|
||||
should_continue = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
should_continue = false;
|
||||
}
|
||||
|
||||
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
|
||||
block.CycleCount()++;
|
||||
|
||||
@@ -8,25 +8,31 @@
|
||||
|
||||
#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 (&s)[N + 1]) {
|
||||
std::array<char, N> r{};
|
||||
for (size_t i = 0; i < N; i++)
|
||||
r[i] = s[i];
|
||||
return r;
|
||||
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;
|
||||
}
|
||||
|
||||
/// @brief Helper functions for the decoders.
|
||||
/// @tparam MatcherT The type of the Matcher to use.
|
||||
/**
|
||||
* 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;
|
||||
@@ -34,15 +40,17 @@ struct detail {
|
||||
|
||||
static constexpr size_t opcode_bitsize = mcl::bitsizeof<opcode_type>;
|
||||
|
||||
/// @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.
|
||||
/**
|
||||
* 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 = opcode_type(1);
|
||||
const auto one = static_cast<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;
|
||||
@@ -93,7 +101,7 @@ struct detail {
|
||||
}
|
||||
#if !defined(DYNARMIC_IGNORE_ASSERTS) && !defined(__ANDROID__)
|
||||
// Avoids a MSVC ICE, and avoids Android NDK issue.
|
||||
DEBUG_ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
|
||||
ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m) { return m != 0; }));
|
||||
#endif
|
||||
return std::make_tuple(masks, shifts);
|
||||
}
|
||||
@@ -101,32 +109,65 @@ 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 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])...);
|
||||
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])...);
|
||||
};
|
||||
}
|
||||
};
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
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);
|
||||
/// @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);
|
||||
}
|
||||
};
|
||||
|
||||
#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)
|
||||
#define DYNARMIC_DECODER_GET_MATCHER(MatcherT, fn, name, bitstring) Decoder::detail::detail<MatcherT<V>>::template GetMatcher<bitstring>(&V::fn)
|
||||
|
||||
} // namespace detail
|
||||
} // namespace Dynarmic::Decoder
|
||||
|
||||
@@ -15,41 +15,50 @@
|
||||
namespace Dynarmic::Decoder {
|
||||
|
||||
/// Generic instruction handling construct.
|
||||
/// @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>
|
||||
/// @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>
|
||||
class Matcher {
|
||||
public:
|
||||
using opcode_type = T;
|
||||
using visitor_type = V;
|
||||
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)} {}
|
||||
|
||||
constexpr Matcher(T mask, T expected) noexcept
|
||||
: mask{mask}
|
||||
, expected{expected}
|
||||
{}
|
||||
|
||||
/// @brief Gets the mask for this instruction.
|
||||
constexpr inline T GetMask() const noexcept {
|
||||
/// Gets the mask for this instruction.
|
||||
inline opcode_type GetMask() const noexcept {
|
||||
return mask;
|
||||
}
|
||||
|
||||
/// @brief Gets the expected value after masking for this instruction.
|
||||
constexpr inline T GetExpected() const noexcept {
|
||||
/// Gets the expected value after masking for this instruction.
|
||||
inline opcode_type GetExpected() const noexcept {
|
||||
return expected;
|
||||
}
|
||||
|
||||
/// @brief Tests to see if the given instruction is the instruction this matcher represents.
|
||||
/// 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.
|
||||
constexpr inline bool Matches(T instruction) const noexcept {
|
||||
inline bool Matches(opcode_type instruction) const noexcept {
|
||||
return (instruction & mask) == expected;
|
||||
}
|
||||
|
||||
T mask;
|
||||
T 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;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Decoder
|
||||
|
||||
@@ -27,8 +27,9 @@ class Imm {
|
||||
public:
|
||||
static constexpr size_t bit_size = bit_size_;
|
||||
|
||||
explicit Imm(u32 value) : value(value) {
|
||||
DEBUG_ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected");
|
||||
explicit Imm(u32 value)
|
||||
: value(value) {
|
||||
ASSERT((mcl::bit::get_bits<0, bit_size - 1>(value) == value) && "More bits in value than expected");
|
||||
}
|
||||
|
||||
template<typename T = u32>
|
||||
|
||||
@@ -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 (instruction & mask) == expect;
|
||||
return m.Matches(instruction);
|
||||
});
|
||||
const bool altiserr = is_decode_error(*alternative, instruction);
|
||||
|
||||
|
||||
@@ -43,12 +43,12 @@
|
||||
using namespace Dynarmic;
|
||||
|
||||
std::string_view GetNameOfA32Instruction(u32 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;
|
||||
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);
|
||||
return "<null>";
|
||||
}
|
||||
|
||||
|
||||
@@ -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,8 +5,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -108,39 +106,18 @@ void ConfigurePerGameAddons::OnItemChanged(QStandardItem* item) {
|
||||
void ConfigurePerGameAddons::ApplyConfiguration() {
|
||||
std::vector<std::string> disabled_addons;
|
||||
|
||||
// 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);
|
||||
for (const auto& item : list_items) {
|
||||
const auto disabled = item.front()->checkState() == Qt::Unchecked;
|
||||
if (disabled) {
|
||||
QVariant userData = item.front()->data(Qt::UserRole);
|
||||
if (userData.isValid() && userData.canConvert<quint32>() &&
|
||||
item->text() == QStringLiteral("Update")) {
|
||||
const quint32 numeric_version = userData.toUInt();
|
||||
item.front()->text() == QStringLiteral("Update")) {
|
||||
quint32 numeric_version = userData.toUInt();
|
||||
disabled_addons.push_back(fmt::format("Update@{}", numeric_version));
|
||||
} else {
|
||||
// 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());
|
||||
}
|
||||
disabled_addons.push_back(item.front()->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];
|
||||
@@ -337,40 +314,19 @@ 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) {
|
||||
@@ -378,18 +334,16 @@ void ConfigurePerGameAddons::LoadConfiguration() {
|
||||
}
|
||||
|
||||
bool patch_disabled = false;
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
bool should_enable = !patch_disabled && !is_incompatible_cheat;
|
||||
bool should_enable = !patch_disabled;
|
||||
|
||||
if (patch.type == FileSys::PatchType::Update) {
|
||||
if (should_enable) {
|
||||
@@ -402,37 +356,12 @@ void ConfigurePerGameAddons::LoadConfiguration() {
|
||||
update_items.push_back(first_item);
|
||||
}
|
||||
|
||||
if (!is_incompatible_cheat) {
|
||||
first_item->setCheckState(should_enable ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
first_item->setCheckState(should_enable ? Qt::Checked : Qt::Unchecked);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
list_items.push_back(QList<QStandardItem*>{
|
||||
first_item, new QStandardItem{QString::fromStdString(patch.version)}});
|
||||
item_model->appendRow(list_items.back());
|
||||
}
|
||||
|
||||
tree_view->expandAll();
|
||||
tree_view->resizeColumnToContents(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user