Compare commits

..

27 Commits

Author SHA1 Message Date
CamilleLaVey 6e85c23070 TEST 2026-09-15 05:02:32 -04:00
CamilleLaVey 36b1b23d40 Experiments on window mirroring + add vertex/index to the virtual page 2026-09-15 03:02:55 -04:00
CamilleLaVey 08ba001529 another try to add compute shaders to the uma path 2026-09-14 22:04:56 -04:00
CamilleLaVey 10a75d80eb Pure and simply meow 2026-09-14 20:15:13 -04:00
CamilleLaVey 8ec648d663 Let's try this on memory reclamations 2026-09-14 20:15:13 -04:00
CamilleLaVey ebedba1f2c Extended the impact on AHB fallback 2026-09-14 20:15:13 -04:00
CamilleLaVey 8ebbdc9bee What if we just rely entirely on SGSR common than using EdgeDir? 2026-09-14 20:15:13 -04:00
CamilleLaVey 0495255867 Another intent 2026-09-14 20:15:13 -04:00
CamilleLaVey 7be3fc3ad5 Just cries internally 2026-09-14 20:15:13 -04:00
CamilleLaVey a4276b598b Meow. 2026-09-14 20:15:13 -04:00
CamilleLaVey 122afb04f7 Adjustments on upload for textures 2026-09-14 20:15:12 -04:00
CamilleLaVey ea3de6ee81 Fix build 2026-09-14 20:15:12 -04:00
CamilleLaVey 32c76f1e0a Fix msvc build 2026-09-14 20:15:12 -04:00
CamilleLaVey bce8e0aa6d [TEST] Bring the unswizzle work from tiled-gpu-v2 x2 + revert of vertex, indexes from AHB path 2026-09-14 20:15:12 -04:00
CamilleLaVey a4864176f5 Just a quick test 2026-09-14 20:15:12 -04:00
CamilleLaVey 1f2aa7e40b Added vertex, uniform and indexes on the upload path 2026-09-14 20:15:12 -04:00
CamilleLaVey ce8c754eb3 Meowly The Build Destroyer 2026-09-14 20:15:12 -04:00
CamilleLaVey 50e59cf09d Another intent on write/read on the fly 2026-09-14 20:15:11 -04:00
CamilleLaVey 5caa0bd567 Changes on the maps counts again 2026-09-14 20:15:11 -04:00
CamilleLaVey d93039fb42 Step back + keeping fixes from unified download + shared mem refactor 2026-09-14 20:15:11 -04:00
CamilleLaVey 17463f7b0d another take 2026-09-14 20:15:11 -04:00
CamilleLaVey 0ebf1ff07c Keep AHB limitations 2026-09-14 20:15:11 -04:00
CamilleLaVey c0c0af6085 [TEST] Another increased on windows size 2026-09-14 20:15:11 -04:00
CamilleLaVey a8589321bc Increased windows size 2026-09-14 20:15:11 -04:00
CamilleLaVey ce864eac38 [TEST] Increased window limiter on AHB 2026-09-14 20:15:11 -04:00
CamilleLaVey 4a3a2c0a11 [TEST] Adjustment AHB to tiled-gpu-v2 state 2026-09-14 20:15:10 -04:00
CamilleLaVey 42636ba127 [memory, vulkan] Initial implementation for Unified Memory 2026-09-14 20:15:10 -04:00
182 changed files with 19397 additions and 17142 deletions
+13
View File
@@ -0,0 +1,13 @@
diff --git a/libs/cobalt/include/boost/cobalt/concepts.hpp b/libs/cobalt/include/boost/cobalt/concepts.hpp
index d49f2ec..a9bdb80 100644
--- a/libs/cobalt/include/boost/cobalt/concepts.hpp
+++ b/libs/cobalt/include/boost/cobalt/concepts.hpp
@@ -62,7 +62,7 @@ struct enable_awaitables
template <typename T>
concept with_get_executor = requires (T& t)
{
- {t.get_executor()} -> asio::execution::executor;
+ t.get_executor();
};
+7 -3
View File
@@ -417,9 +417,12 @@ AddJsonPackage(boost)
set(BOOST_NO_HEADERS ${Boost_ADDED}) set(BOOST_NO_HEADERS ${Boost_ADDED})
if (Boost_ADDED) if (Boost_ADDED)
add_compile_definitions(YUZU_BOOST_v1) if (MSVC OR ANDROID)
add_compile_definitions(YUZU_BOOST_v1)
endif()
if (NOT MSVC OR CXX_CLANG) if (NOT MSVC OR CXX_CLANG)
# solaris sucks # boost sucks
if (SOLARIS) if (SOLARIS)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>) add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
endif() endif()
@@ -428,7 +431,8 @@ if (Boost_ADDED)
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>) target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
target_compile_options(boost_asio INTERFACE target_compile_options(boost_asio INTERFACE
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion> $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>) $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
)
endif() endif()
endif() endif()
+9 -16
View File
@@ -5,25 +5,23 @@
"repo": "lioncash/biscuit", "repo": "lioncash/biscuit",
"version": "v0.19.0" "version": "v0.19.0"
}, },
"atmosphere_zstd_bic": {
"hash": "204f7573ea40de2878759c7ff5b2c016111fc29ae8574df50a3a0db9018c3b012f9040ea7466559130424d4335f2dfd6d7abfdc4d4d67e8a34531b8284f0cc6a",
"repo": "Atmosphere-NX/Atmosphere",
"version": "3ace80cc7b87e1837fccca1522801bbf6fddc6aa"
},
"boost": { "boost": {
"artifact": "boost-%VERSION%.tar.zst", "artifact": "%VERSION%-cmake.tar.xz",
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem", "find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
"hash": "681d97be4386767662e230b2e7c9754d45e709cc5db4e747c23c27d1f5b0e87770bdf19b0bc44dcb0e8349324887bbb8fffb432098f4d0ce74a5043021a90afc", "hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
"min_version": "1.57", "min_version": "1.57",
"package": "Boost", "package": "Boost",
"repo": "eden-emulator/ext-boost", "patches": [
"version": "1.92.0" "0001-clang-cl.patch"
],
"repo": "boostorg/boost",
"version": "boost-1.90.0"
}, },
"boost_headers": { "boost_headers": {
"bundled": true, "bundled": true,
"hash": "c5fa2cd72f6e6666b7963b97bc359c75284b8fb540c30f3629a028b85270c9bc66c8a051383964f2bd4c1e005a4691593d15696e7ef39ea87cf6cff9e5691fb2", "hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
"repo": "boostorg/headers", "repo": "boostorg/headers",
"version": "boost-1.91.0" "version": "boost-1.90.0"
}, },
"catch2": { "catch2": {
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35", "hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
@@ -338,11 +336,6 @@
"repo": "herumi/xbyak", "repo": "herumi/xbyak",
"version": "v7.40.1" "version": "v7.40.1"
}, },
"zbic": {
"hash": "fbe2f37986377d7f0d96ae3224c80b5971df6e8b6961f68061f1975ebb2e8cb78f07b90f014b007be4023dbf5513581504e7f7d61a5237cb2a8a7a61ae11e482",
"repo": "kinnay/zbic",
"version": "11b08f2712264bbed731545085cbd9702096ceb7"
},
"zlib": { "zlib": {
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4", "hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
"min_version": "1.2", "min_version": "1.2",
+579 -624
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+503 -548
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+508 -550
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+527 -573
View File
File diff suppressed because it is too large Load Diff
+502 -547
View File
File diff suppressed because it is too large Load Diff
+21 -33
View File
@@ -1,44 +1,32 @@
# User Handbook - Command Line # User Handbook - Command Line
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments. There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments.
## eden
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application. - `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234` - `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
- `--config/-c`: Specify alternate configuration file. - `-f`: Use fullscreen.
- `--fullscreen/-f`: Use fullscreen. - `-u <number>`: Select the index of the user to load as.
- `--help/-h`: Display help. - `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
- `--game/-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
- `--multiplayer/-m`: Specify multiplayer options.
- `--program/-p`: Specify the program arguments to pass (optional).
- `--user/-u <number>`: Specify the user index.
- `--version/-v`: Display version and quit.
- `--input-profile/-i <name>`: Specifies input profile name to use (for player #0 only).
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
- `--filter/-x`: Sets the debug log filter irrespective of settings.
- `--singlecore/-s`: Forces single-core regardless of settings.
Only the Qt frontend supports the following arguments:
- `-qlaunch`: Launch QLaunch. - `-qlaunch`: Launch QLaunch.
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`. - `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
- Requires a copy of Atmosphere to be extracted onto `sdmc`. - Requires a copy of Atmosphere to be extracted onto `sdmc`.
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`. - This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
- `-setup`: Launch setup applet. - `-setup`: Launch setup applet.
`eden` (provided with `--room`), and `eden-room` supports the following options as well: ## eden-cli
- `-n/--room-name`: The name of the room. - `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
- `-d/--room-description`: The room description. - `--config/-c`: Specify alternate configuration file.
- `-s/--bind-address`: The bind address for the room. - `--fullscreen/-f`: Set fullscreen.
- `-p/--port`: The port used for the room. - `--help/-h`: Display help.
- `-m/--max-members`: The maximum number of players for this room. - `--game/-g`: Specify the game to run.
- `-w/--password`: The password for the room. - `--multiplayer/-m`: Specify multiplayer options.
- `-g/--preferred-game`: The preferred game for this room. - `--program/-p`: Specify the program arguments to pass (optional).
- `-i/--preferred-game-id`: The preferred game-id for this room. - `--user/-u`: Specify the user index.
- `-u/--username`: The username used for announce. - `--version/-v`: Display version and quit.
- `-t/--token`: The token used for announce. - `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
- `-a/--web-api-url`: yuzu Web API url. - `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
- `-b/--ban-list-file`: The file for storing the room ban list. - `--filter/-x`: Sets the debug log filter irrespective of settings.
- `-l/--log-file`: The file for storing the room log. - `--singlecore/-s`: Forces single-core regardless of settings.
- `-h/--help`: Display this help and exit.
- `-v/--version`: Output version information and exit.
-5
View File
@@ -48,11 +48,6 @@ if (NOT TARGET stb::headers)
add_library(stb::headers ALIAS stb) add_library(stb::headers ALIAS stb)
endif() endif()
AddJsonPackage(NAME zbic DOWNLOAD_ONLY)
set(ZBIC_INCLUDE_DIR
"${zbic_SOURCE_DIR}/src"
PARENT_SCOPE)
# ItaniumDemangle (Windows only) # ItaniumDemangle (Windows only)
if (WIN32 AND NOT TARGET LLVM::Demangle) if (WIN32 AND NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp) add_library(demangle demangle/ItaniumDemangle.cpp)
@@ -27,6 +27,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"), RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
RENDERER_ASYNC_PRESENTATION("async_presentation"), RENDERER_ASYNC_PRESENTATION("async_presentation"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"), RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_UNIFIED_MEMORY("use_unified_memory"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"), ENABLE_BUFFER_HISTORY("enable_buffer_history"),
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"), USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
@@ -785,6 +785,13 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_asynchronous_shaders_description descriptionId = R.string.renderer_asynchronous_shaders_description
) )
) )
put(
SwitchSetting(
BooleanSetting.RENDERER_UNIFIED_MEMORY,
titleId = R.string.renderer_unified_memory,
descriptionId = R.string.renderer_unified_memory_description
)
)
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.FAST_GPU_TIME, IntSetting.FAST_GPU_TIME,
@@ -558,6 +558,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.FIX_BLOOM_EFFECTS.key) add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.EMULATE_BGR565.key) add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(BooleanSetting.RENDERER_UNIFIED_MEMORY.key)
add(IntSetting.ANDROID_PIPELINE_WORKERS.key) add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key) add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
+2 -2
View File
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { jstring jupdatePath) {
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId)); u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
std::string updatePath = Common::Android::GetJString(env, jupdatePath); std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
for (const auto& item : nsp->GetNCAs()) { for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) { for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) { if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId()); auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
if (update_id == program_id) { if (update_id == program_id) {
return true; return true;
} }
@@ -289,7 +289,6 @@
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string> <string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string> <string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string> <string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
<string name="post_processing_add">Añadir efecto</string>
<string name="post_processing_open_list">Abrir lista</string> <string name="post_processing_open_list">Abrir lista</string>
<string name="post_processing_close_list">Cerrar lista</string> <string name="post_processing_close_list">Cerrar lista</string>
<string name="post_processing_reset">Restablecer a predeterminado</string> <string name="post_processing_reset">Restablecer a predeterminado</string>
@@ -318,13 +318,6 @@
<string name="frame_gen_target_rate">Целевая частота кадров</string> <string name="frame_gen_target_rate">Целевая частота кадров</string>
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string> <string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string> <string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
<string name="frame_gen_on">Вкл.</string>
<string name="frame_gen_off">Выкл.</string>
<string name="frame_gen_fixed">Фикс.</string>
<string name="frame_gen_flow_auto">Авто</string>
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string> <string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string> <string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string> <string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
@@ -335,14 +328,12 @@
<string name="frame_gen_flow_scale">Разрешение оценки движения</string> <string name="frame_gen_flow_scale">Разрешение оценки движения</string>
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string> <string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
<string name="frame_gen_unsupported">Генерация кадров недоступна</string> <string name="frame_gen_unsupported">Генерация кадров недоступна</string>
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string> <string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
<string name="lossless_scaling_install">Установить Lossless.dll</string> <string name="lossless_scaling_install">Установить Lossless.dll</string>
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string> <string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string> <string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
<string name="frame_generation_support">Генерация кадров</string> <string name="frame_generation_support">Генерация кадров</string>
<string name="frame_generation_supported">Поддерживается</string> <string name="frame_generation_supported">Поддерживается</string>
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string> <string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
<string name="lossless_scaling_installed">Установлена</string> <string name="lossless_scaling_installed">Установлена</string>
<string name="lossless_scaling_not_installed">Не установлена</string> <string name="lossless_scaling_not_installed">Не установлена</string>
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
<string name="notice_notification_channel_name">错误与注意事项</string> <string name="notice_notification_channel_name">错误与注意事项</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string> <string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string> <string name="notification_permission_not_granted">未授予通知权限!</string>
@@ -291,25 +290,6 @@
<string name="gpu_driver_fetcher">GPU驱动获取器</string> <string name="gpu_driver_fetcher">GPU驱动获取器</string>
<string name="gpu_driver_manager">GPU 驱动管理器</string> <string name="gpu_driver_manager">GPU 驱动管理器</string>
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string> <string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
<string name="post_processing">后处理效果</string>
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
<string name="post_processing_add">添加效果</string>
<string name="post_processing_remove">移除</string>
<string name="post_processing_open_list">打开列表</string>
<string name="post_processing_close_list">关闭列表</string>
<string name="post_processing_remove_all">移除效果</string>
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
<string name="post_processing_presets">预设</string>
<string name="post_processing_preset_new">新建预设</string>
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
<string name="post_processing_preset_none">无预设</string>
<string name="post_processing_preset_delete">删除预设</string>
<string name="post_processing_preset_reset">重置预设值</string>
<string name="post_processing_reset">重置为默认值</string>
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
<string name="frame_gen">帧生成</string> <string name="frame_gen">帧生成</string>
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string> <string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string> <string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
@@ -324,13 +304,6 @@
<string name="frame_gen_target_rate_60">60 FPS</string> <string name="frame_gen_target_rate_60">60 FPS</string>
<string name="frame_gen_target_rate_90">90 FPS</string> <string name="frame_gen_target_rate_90">90 FPS</string>
<string name="frame_gen_target_rate_120">120 FPS</string> <string name="frame_gen_target_rate_120">120 FPS</string>
<string name="frame_gen_on">开</string>
<string name="frame_gen_off">关</string>
<string name="frame_gen_fixed">固定</string>
<string name="frame_gen_flow_auto">自动</string>
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
<string name="frame_gen_queue_target">帧队列目标</string> <string name="frame_gen_queue_target">帧队列目标</string>
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string> <string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string> <string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
@@ -341,14 +314,12 @@
<string name="frame_gen_flow_scale">运动预估分辨率</string> <string name="frame_gen_flow_scale">运动预估分辨率</string>
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string> <string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
<string name="frame_gen_unsupported">帧生成不可用</string> <string name="frame_gen_unsupported">帧生成不可用</string>
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string> <string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
<string name="lossless_scaling_install">安装 Lossless.dll</string> <string name="lossless_scaling_install">安装 Lossless.dll</string>
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string> <string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string> <string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
<string name="frame_generation_support">帧生成</string> <string name="frame_generation_support">帧生成</string>
<string name="frame_generation_supported">支持</string> <string name="frame_generation_supported">支持</string>
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
<string name="lossless_scaling">无损缩放</string> <string name="lossless_scaling">无损缩放</string>
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string> <string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
<string name="lossless_scaling_installed">已安装</string> <string name="lossless_scaling_installed">已安装</string>
@@ -640,11 +611,6 @@
<string name="log">日志记录</string> <string name="log">日志记录</string>
<string name="flush_by_line">按行刷新调试日志</string> <string name="flush_by_line">按行刷新调试日志</string>
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string> <string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
<string name="extended_logging">开启扩展日志</string>
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
<string name="log_filter">日志筛选器</string>
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
<!-- GPU Logging strings --> <!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU 日志</string> <string name="gpu_logging_header">GPU 日志</string>
<string name="gpu_log_level">日志等级</string> <string name="gpu_log_level">日志等级</string>
@@ -928,8 +894,6 @@
<string name="loader_error_file_not_found">ROM 文件不存在</string> <string name="loader_error_file_not_found">ROM 文件不存在</string>
<string name="loader_requires_firmware">游戏需要固件</string> <string name="loader_requires_firmware">游戏需要固件</string>
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
<!-- Intent Launch strings --> <!-- Intent Launch strings -->
<string name="searching_for_game">正在搜索游戏...</string> <string name="searching_for_game">正在搜索游戏...</string>
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string> <string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
@@ -229,7 +229,7 @@
<string name="add_directory_success">遊戲目錄新增成功</string> <string name="add_directory_success">遊戲目錄新增成功</string>
<string name="enable_update_checks">檢查更新</string> <string name="enable_update_checks">檢查更新</string>
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string> <string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
<string name="update_available">發現新版本</string> <string name="update_available">偵測到新版本</string>
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string> <string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
<string name="downloading_update">下載更新中</string> <string name="downloading_update">下載更新中</string>
<string name="update_download_failed">更新下載失敗</string> <string name="update_download_failed">更新下載失敗</string>
@@ -1020,40 +1020,9 @@
<string name="memory_6gb">6GB (不穩定)</string> <string name="memory_6gb">6GB (不穩定)</string>
<string name="memory_8gb">8GB (不穩定)</string> <string name="memory_8gb">8GB (不穩定)</string>
<!-- CPU clock levels -->
<string name="clock_normal">正常</string>
<string name="clock_boost">加速</string>
<string name="clock_fast">超頻</string>
<!-- GPU clock levels -->
<string name="fast_gpu_normal">正常</string>
<string name="fast_gpu_medium">加速</string>
<string name="fast_gpu_high">超頻</string>
<!-- GPU swizzle texture size -->
<string name="gpu_texturesizeswizzle_verysmall">極小 (16MB)</string>
<string name="gpu_texturesizeswizzle_small">小(32MB)</string>
<string name="gpu_texturesizeswizzle_normal">正常(128MB)</string>
<string name="gpu_texturesizeswizzle_large">大(256MB)</string>
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB)</string>
<!-- GPU swizzle streams -->
<string name="gpu_swizzle_verylow">極小(4MB)</string>
<string name="gpu_swizzle_low">小(8MB)</string>
<string name="gpu_swizzle_normal">正常(16MB)</string>
<string name="gpu_swizzle_medium">中(32MB)</string>
<string name="gpu_swizzle_high">大(64MB)</string>
<!-- GPU swizzle chunks -->
<string name="gpu_swizzlechunk_verylow">極小(32)</string>
<string name="gpu_swizzlechunk_low">小(64)</string>
<string name="gpu_swizzlechunk_normal">正常(128)</string>
<string name="gpu_swizzlechunk_medium">中(256)</string>
<string name="gpu_swizzlechunk_high">大(512)</string>
<!-- Temperature Units --> <!-- Temperature Units -->
<string name="temperature_celsius">攝氏</string> <string name="temperature_celsius">攝氏度</string>
<string name="temperature_fahrenheit">華氏</string> <string name="temperature_fahrenheit">華氏度</string>
<!-- Memory Sizes --> <!-- Memory Sizes -->
<string name="memory_byte_shorthand">B</string> <string name="memory_byte_shorthand">B</string>
@@ -1066,8 +1035,6 @@
<string name="renderer_none">無</string> <string name="renderer_none">無</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">效能</string>
<string name="renderer_accuracy_high">準確</string> <string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy --> <!-- DMA Accuracy -->
@@ -605,6 +605,8 @@
<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">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string> <string name="renderer_asynchronous_shaders">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="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
<string name="renderer_unified_memory">Unified memory access</string>
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string> <string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string> <string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string> <string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
+2 -6
View File
@@ -76,6 +76,7 @@ add_library(
logging.h logging.h
lz4_compression.cpp lz4_compression.cpp
lz4_compression.h lz4_compression.h
make_unique_for_overwrite.h
math_util.h math_util.h
memory_detect.cpp memory_detect.cpp
memory_detect.h memory_detect.h
@@ -109,6 +110,7 @@ add_library(
socket_types.h socket_types.h
sparse_large_vector.cpp sparse_large_vector.cpp
sparse_large_vector.h sparse_large_vector.h
spin_lock.h
stb.cpp stb.cpp
stb.h stb.h
steady_clock.cpp steady_clock.cpp
@@ -137,8 +139,6 @@ add_library(
uuid.cpp uuid.cpp
uuid.h uuid.h
vector_math.h vector_math.h
zbic_compression.cpp
zbic_compression.h
zstd_compression.cpp zstd_compression.cpp
zstd_compression.h zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp fs/ryujinx_compat.h fs/ryujinx_compat.cpp
@@ -149,10 +149,6 @@ add_library(
net/net.h net/net.cpp net/net.h net/net.cpp
container/unordered_map.h container/unordered_set.h) container/unordered_map.h container/unordered_set.h)
set_source_files_properties(zbic_compression.cpp PROPERTIES
INCLUDE_DIRECTORIES "${ZBIC_INCLUDE_DIR}"
COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:Clang,GNU>:-Wno-unused-function>")
if(WIN32) if(WIN32)
target_sources(common PRIVATE windows/timer_resolution.cpp target_sources(common PRIVATE windows/timer_resolution.cpp
windows/timer_resolution.h) windows/timer_resolution.h)
+3 -4
View File
@@ -244,8 +244,7 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0} , ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0} , us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0} , ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1} , rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0} , cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0} , gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
, invariant{invariant_} , invariant{invariant_}
@@ -292,7 +291,7 @@ bool WallClock::IsNative() const {
} }
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const { u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor); return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
} }
#elif defined(HAS_NCE) #elif defined(HAS_NCE)
namespace { namespace {
@@ -417,7 +416,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
const WallClock g_wall_clock = [] { const WallClock g_wall_clock = [] {
#if defined(ARCHITECTURE_x86_64) #if defined(ARCHITECTURE_x86_64)
auto const& caps = Common::g_cpu_caps; auto const& caps = Common::g_cpu_caps;
return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency); return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
#elif defined(HAS_NCE) #elif defined(HAS_NCE)
return WallClock(false, 1); return WallClock(false, 1);
#else #else
-1
View File
@@ -96,7 +96,6 @@ public:
u64 ns_rdtsc_factor; u64 ns_rdtsc_factor;
u64 us_rdtsc_factor; u64 us_rdtsc_factor;
u64 ms_rdtsc_factor; u64 ms_rdtsc_factor;
u64 rdtsc_ns_integer;
u64 rdtsc_ns_factor; u64 rdtsc_ns_factor;
u64 cntpct_rdtsc_factor; u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor; u64 gputick_rdtsc_factor;
+20 -18
View File
@@ -17,33 +17,35 @@
namespace Common { namespace Common {
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
// We don't need to use the preprocessor, we can just select depending on return type
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(int r, char *err_str) {
return std::string{r != 0
? "(strerror_r failed to format error)"
: err_str};
}
std::string NativeErrorToString(int e) { std::string NativeErrorToString(int e) {
#ifdef _WIN32 #ifdef _WIN32
LPSTR err_str; LPSTR err_str;
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPSTR(&err_str), 1, nullptr); reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
if (res) { if (!res) {
std::string ret(err_str); return "(FormatMessageA failed to format error)";
LocalFree(err_str);
return ret;
} }
return "(FormatMessageA failed to format error)"; std::string ret(err_str);
LocalFree(err_str);
return ret;
#else #else
char err_str[255]; char err_str[255];
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str); #if defined(__ANDROID__) || \
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
// Thread safe (GNU-specific)
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);
#else
// Thread safe (XSI-compliant)
int second_err = strerror_r(e, err_str, sizeof(err_str));
if (second_err != 0) {
return "(strerror_r failed to format error)";
}
return std::string(err_str);
#endif // GLIBC etc.
#endif // _WIN32 #endif // _WIN32
} }
+12 -65
View File
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <cctype>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include "common/container/unordered_map.h" #include "common/container/unordered_map.h"
@@ -481,80 +480,28 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end()); path.end());
std::string root; const bool absolute = !path.empty() && path[0] == type2;
std::string_view components{path};
bool drive_relative = false;
#ifdef _WIN32
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
const bool drive =
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
if (network) {
root.assign(2, type2);
components.remove_prefix(2);
} else if (drive) {
root.assign(path.data(), 2);
components.remove_prefix(2);
if (!components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
} else {
drive_relative = true;
}
}
#endif
if (root.empty() && !components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
}
const auto path_parts = SplitPathComponents(components);
std::size_t root_component_count = 0;
#ifdef _WIN32
if (network) {
root_component_count = 2;
const auto is_unc = [](std::string_view part) {
return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') &&
(part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c');
};
if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) {
root_component_count = 4;
}
}
#endif
std::vector<std::string_view> parts; std::vector<std::string_view> parts;
for (std::size_t i = 0; i < path_parts.size(); ++i) {
const auto part = path_parts[i]; for (const auto part : SplitPathComponents(path))
if (i < root_component_count) { {
parts.push_back(part); if (part.empty() || part == ".")
} else if (part.empty() || part == ".") {
continue; continue;
} else if (part == "..") { if (part == ".." && !parts.empty() && parts.back() != "..")
if (parts.size() > root_component_count) { parts.pop_back();
parts.pop_back(); else if (part != "..") parts.push_back(part);
}
} else {
parts.push_back(part);
}
} }
const std::size_t root_length = root.size(); std::string resolved = absolute ? std::string(1, type2) : std::string{};
std::string resolved = std::move(root); for (std::size_t i = 0; i < parts.size(); ++i)
for (std::size_t i = 0; i < parts.size(); ++i) { {
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative)) if (i != 0)
resolved += type2; resolved += type2;
resolved.append(parts[i].data(), parts[i].size()); resolved.append(parts[i].data(), parts[i].size());
} }
path = std::move(resolved); path = std::move(resolved);
if (!path.empty() && path.size() == root_length) {
return path;
}
return std::string(RemoveTrailingSlash(path)); return std::string(RemoveTrailingSlash(path));
} }
+2 -3
View File
@@ -347,9 +347,8 @@ enum class DirectorySeparator {
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename); [[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are // depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
// preserved.
[[nodiscard]] std::string SanitizePath( [[nodiscard]] std::string SanitizePath(
std::string_view path, std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+22 -28
View File
@@ -4,34 +4,17 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include "common/heap_tracker.h" #include "common/heap_tracker.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/memory_detect.h"
#include "common/assert.h" #include "common/assert.h"
namespace Common { namespace Common {
namespace {
s64 GetMaxPermissibleResidentMapCount() {
// Default value.
s64 value = 65530;
// Try to read how many mappings we can make.
std::ifstream s("/proc/sys/vm/max_map_count");
s >> value;
// Print, for debug.
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
// Allow 20000 maps for other code and to account for split inaccuracy.
return std::max<s64>(value - 20000, 0);
}
} // namespace
HeapTracker::HeapTracker(Common::HostMemory& buffer) HeapTracker::HeapTracker(Common::HostMemory& buffer)
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {} : m_buffer(buffer),
m_has_hardware_buffer_backing(!buffer.BackingHardwareBuffers().empty()),
m_max_resident_map_count(static_cast<s64>(GetPermissibleMapCount())) {}
HeapTracker::~HeapTracker() = default; HeapTracker::~HeapTracker() = default;
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length, void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
@@ -85,7 +68,8 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
// If resident, erase from resident map. // If resident, erase from resident map.
if (item->is_resident) { if (item->is_resident) {
ASSERT(--m_resident_map_count >= 0); m_resident_map_count -= this->HostMapCount(item->paddr, item->size);
ASSERT(m_resident_map_count >= 0);
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item)); m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
} }
@@ -191,7 +175,7 @@ bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
// This map is now resident. // This map is now resident.
it->is_resident = true; it->is_resident = true;
m_resident_map_count++; m_resident_map_count += this->HostMapCount(it->paddr, it->size);
m_resident_mappings.insert(*it); m_resident_mappings.insert(*it);
} }
@@ -213,17 +197,17 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
// Despite being worse in theory, this has proven to be better in practice than more // Despite being worse in theory, this has proven to be better in practice than more
// regularly dumping a smaller amount, because it significantly reduces average case // regularly dumping a smaller amount, because it significantly reduces average case
// lock contention. // lock contention.
std::size_t const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2; s64 const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
std::size_t const evict_count = m_resident_map_count - desired_count;
auto it = m_resident_mappings.begin(); auto it = m_resident_mappings.begin();
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) { while (m_resident_map_count > desired_count && it != m_resident_mappings.end()) {
// Unmark and unmap. // Unmark and unmap.
it->is_resident = false; it->is_resident = false;
m_buffer.Unmap(it->vaddr, it->size, false); m_buffer.Unmap(it->vaddr, it->size, false);
// Advance. // Advance.
ASSERT(--m_resident_map_count >= 0); m_resident_map_count -= this->HostMapCount(it->paddr, it->size);
ASSERT(m_resident_map_count >= 0);
it = m_resident_mappings.erase(it); it = m_resident_mappings.erase(it);
} }
} }
@@ -245,6 +229,7 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
// Cache the original values. // Cache the original values.
auto* const left = std::addressof(*it); auto* const left = std::addressof(*it);
const size_t orig_size = left->size; const size_t orig_size = left->size;
const s64 orig_host_map_count = this->HostMapCount(left->paddr, orig_size);
// Adjust the left map. // Adjust the left map.
const size_t left_size = offset - left->vaddr; const size_t left_size = offset - left->vaddr;
@@ -266,11 +251,20 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
// If resident, also insert into resident map. // If resident, also insert into resident map.
if (right->is_resident) { if (right->is_resident) {
m_resident_map_count++; m_resident_map_count += this->HostMapCount(left->paddr, left->size) +
this->HostMapCount(right->paddr, right->size) -
orig_host_map_count;
m_resident_mappings.insert(*right); m_resident_mappings.insert(*right);
} }
} }
s64 HeapTracker::HostMapCount(PAddr paddr, size_t size) const {
if (!m_has_hardware_buffer_backing) {
return size != 0 ? 1 : 0;
}
return static_cast<s64>(m_buffer.BackingMapCount(paddr, size));
}
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) { HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
const SeparateHeapMap key{ const SeparateHeapMap key{
.vaddr = offset, .vaddr = offset,
+6
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -82,10 +85,13 @@ private:
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset); AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
s64 HostMapCount(PAddr paddr, size_t size) const;
void RebuildSeparateHeapAddressSpace(); void RebuildSeparateHeapAddressSpace();
private: private:
Common::HostMemory& m_buffer; Common::HostMemory& m_buffer;
const bool m_has_hardware_buffer_backing;
const s64 m_max_resident_map_count; const s64 m_max_resident_map_count;
std::shared_mutex m_rebuild_lock{}; std::shared_mutex m_rebuild_lock{};
+393 -6
View File
@@ -51,14 +51,65 @@
#endif // ^^^ POSIX ^^^ #endif // ^^^ POSIX ^^^
#include <atomic>
#include <mutex> #include <mutex>
#include <random> #include <random>
#include <vector>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/free_region_manager.h" #include "common/free_region_manager.h"
#include "common/host_memory.h" #include "common/host_memory.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/memory_detect.h"
#include "common/settings.h"
#ifdef __ANDROID__
#include <cerrno>
#include <dlfcn.h>
#include <sys/ioctl.h>
#include <android/hardware_buffer.h>
namespace {
struct NativeHandle {
int version;
int numFds;
int numInts;
int data[1];
};
using PFN_AHardwareBuffer_getNativeHandle = const NativeHandle* (*)(const AHardwareBuffer*);
PFN_AHardwareBuffer_getNativeHandle ResolveGetNativeHandle() {
void* const lib = dlopen("libnativewindow.so", RTLD_NOW);
if (lib == nullptr) {
return nullptr;
}
return reinterpret_cast<PFN_AHardwareBuffer_getNativeHandle>(
dlsym(lib, "AHardwareBuffer_getNativeHandle"));
}
struct DmaBufSync {
u64 flags;
};
constexpr u64 DmaBufSyncRead = 1ULL << 0;
constexpr u64 DmaBufSyncWrite = 1ULL << 1;
constexpr u64 DmaBufSyncStart = 0ULL << 2;
constexpr u64 DmaBufSyncEnd = 1ULL << 2;
void SyncDmaBufCpuAccess(int fd, u64 phase) {
DmaBufSync sync{.flags = phase | DmaBufSyncRead | DmaBufSyncWrite};
while (ioctl(fd, _IOW('b', 0, DmaBufSync), &sync) != 0) {
if (errno != EINTR) {
return;
}
}
}
} // namespace
#endif
#if defined(__ANDROID__) && __ANDROID_API__ < 30 #if defined(__ANDROID__) && __ANDROID_API__ < 30
#include <sys/syscall.h> #include <sys/syscall.h>
@@ -75,6 +126,12 @@ namespace Common {
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000; [[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000; [[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
static std::atomic<u64> committed_backing_size{};
u64 GetCommittedBackingSize() noexcept {
return committed_backing_size.load(std::memory_order_relaxed);
}
#ifdef _WIN32 #ifdef _WIN32
// Manually imported for MinGW compatibility // Manually imported for MinGW compatibility
@@ -122,7 +179,7 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn
class HostMemory::Impl { class HostMemory::Impl {
public: public:
explicit Impl(size_t backing_size_, size_t virtual_size_) explicit Impl(size_t backing_size_, size_t virtual_size_, size_t)
: backing_size{backing_size_} : backing_size{backing_size_}
, virtual_size{virtual_size_} , virtual_size{virtual_size_}
, process{GetCurrentProcess()} , process{GetCurrentProcess()}
@@ -236,6 +293,10 @@ public:
UNREACHABLE(); UNREACHABLE();
} }
bool IsBackingShared() const noexcept {
return true;
}
const size_t backing_size; ///< Size of the backing memory in bytes const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
@@ -512,9 +573,10 @@ static int shm_open_anon(int flags, mode_t mode) {
class HostMemory::Impl { class HostMemory::Impl {
public: public:
explicit Impl(size_t backing_size_, size_t virtual_size_) explicit Impl(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
: backing_size{backing_size_} : backing_size{backing_size_}
, virtual_size{virtual_size_} , virtual_size{virtual_size_}
, preferred_offset{preferred_offset_}
{} {}
bool Init() { bool Init() {
@@ -554,10 +616,15 @@ public:
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones"); LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0)); backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
if (fd > 0) { if (fd > 0) {
fd = -1;
close(fd); close(fd);
} }
fd = -1;
} else { } else {
#ifdef __ANDROID__
if (InitAhbBacking()) {
return InitVirtual();
}
#endif
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0)); backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
} }
if (backing_base == MAP_FAILED) { if (backing_base == MAP_FAILED) {
@@ -565,7 +632,10 @@ public:
return false; return false;
} }
// Virtual memory initialization return InitVirtual();
}
bool InitVirtual() {
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size)); virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
if (virtual_base == MAP_FAILED) { if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
@@ -578,6 +648,244 @@ public:
return true; return true;
} }
#ifdef __ANDROID__
static AHardwareBuffer_Desc MakeBlobDesc(size_t len) {
return AHardwareBuffer_Desc{
.width = static_cast<u32>(len),
.height = 1,
.layers = 1,
.format = AHARDWAREBUFFER_FORMAT_BLOB,
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
.stride = 0,
.rfu0 = 0,
.rfu1 = 0,
};
}
static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) {
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
return false;
}
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
AHardwareBuffer_release(buffer);
return false;
}
const int probe_fd = handle->data[0];
bool ok = true;
const auto try_map = [&](int prot, off_t offset) {
if (!ok) {
return;
}
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
if (ptr == MAP_FAILED) {
ok = false;
return;
}
munmap(ptr, PageAlignment);
};
try_map(PROT_READ | PROT_WRITE, 0);
try_map(PROT_READ | PROT_WRITE, static_cast<off_t>(PageAlignment));
#ifdef ARCHITECTURE_arm64
try_map(PROT_READ | PROT_EXEC, 0);
#endif
AHardwareBuffer_release(buffer);
return ok;
}
size_t ComputeAhbBudget(size_t window_size) const {
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
constexpr u64 BaselineFootprint = 6ULL << 30;
if (total_physical <= BaselineFootprint) {
return 0;
}
const u64 permissible_maps = Common::GetPermissibleMapCount();
if (permissible_maps == 0) {
return 0;
}
u64 budget = (total_physical - BaselineFootprint) / 2;
constexpr u64 MapSlotsPerWindow = 64;
const u64 affordable_windows = permissible_maps / MapSlotsPerWindow;
budget = (std::min)(budget, affordable_windows * window_size);
const u64 available = Common::GetAvailablePhysicalMemory();
if (available != 0) {
budget = (std::min)(budget, available / 2);
}
budget = (std::min)(budget, static_cast<u64>(backing_size));
budget = Common::AlignDown(budget, window_size);
constexpr u64 MinimumBudget = 256ULL << 20;
if (budget < MinimumBudget) {
return 0;
}
return static_cast<size_t>(budget);
}
bool InitAhbBacking() {
if (!Settings::values.use_unified_memory.GetValue()) {
return false;
}
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
ResolveGetNativeHandle();
if (get_native_handle == nullptr) {
return false;
}
constexpr size_t window_size = 256ULL << 20;
const size_t budget = ComputeAhbBudget(window_size);
if (budget == 0) {
return false;
}
if (!ProbeAhbBacking(get_native_handle)) {
return false;
}
const size_t aligned_backing = Common::AlignDown(backing_size, window_size);
const size_t max_windows = (std::min)(budget, aligned_backing) / window_size;
std::vector<AHardwareBuffer*> buffers;
std::vector<int> buffer_fds;
const auto cleanup = [&] {
for (AHardwareBuffer* buffer : buffers) {
AHardwareBuffer_release(buffer);
}
buffers.clear();
buffer_fds.clear();
};
for (size_t i = 0; i < max_windows; ++i) {
const AHardwareBuffer_Desc desc = MakeBlobDesc(window_size);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
break;
}
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
AHardwareBuffer_release(buffer);
break;
}
const int buffer_fd = handle->data[0];
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
if (buffer_len < static_cast<off_t>(window_size)) {
AHardwareBuffer_release(buffer);
break;
}
buffers.push_back(buffer);
buffer_fds.push_back(buffer_fd);
}
const size_t num_windows = buffers.size();
if (num_windows == 0) {
return false;
}
const size_t region_size = num_windows * window_size;
const size_t region_base = Common::AlignDown(
(std::min)(preferred_offset, aligned_backing - region_size), window_size);
u8* const base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (base == MAP_FAILED) {
cleanup();
return false;
}
const auto map_over_reservation = [&](size_t offset, size_t len, int map_fd,
off_t map_offset) {
if (len == 0) {
return true;
}
if (mmap(base + offset, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, map_fd,
map_offset) == MAP_FAILED) {
munmap(base, backing_size);
cleanup();
return false;
}
return true;
};
if (!map_over_reservation(0, region_base, fd, 0)) {
return false;
}
for (size_t i = 0; i < num_windows; ++i) {
if (!map_over_reservation(region_base + i * window_size, window_size, buffer_fds[i],
0)) {
return false;
}
}
const size_t tail_offset = region_base + region_size;
if (!map_over_reservation(tail_offset, backing_size - tail_offset, fd,
static_cast<off_t>(tail_offset))) {
return false;
}
backing_base = base;
ahb_windows = std::move(buffers);
ahb_fds = std::move(buffer_fds);
ahb_window_size = window_size;
ahb_base = region_base;
ahb_bytes = region_size;
committed_backing_size.store(region_size, std::memory_order_relaxed);
for (const int window_fd : ahb_fds) {
SyncDmaBufCpuAccess(window_fd, DmaBufSyncStart);
}
return true;
}
void MapBackingRange(size_t virtual_offset, size_t host_offset, size_t length, int prot_flags) {
while (length > 0) {
int map_fd = fd;
off_t map_offset = static_cast<off_t>(host_offset);
size_t chunk = length;
if (host_offset < ahb_base) {
chunk = (std::min)(chunk, ahb_base - host_offset);
} else if (host_offset < ahb_base + ahb_bytes) {
const size_t relative = host_offset - ahb_base;
const size_t window = relative / ahb_window_size;
const size_t local = relative % ahb_window_size;
map_fd = ahb_fds[window];
map_offset = static_cast<off_t>(local);
chunk = (std::min)(chunk, ahb_window_size - local);
}
void* const ret = mmap(virtual_base + virtual_offset, chunk, prot_flags,
MAP_SHARED | MAP_FIXED, map_fd, map_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
virtual_offset += chunk;
host_offset += chunk;
length -= chunk;
}
}
size_t BackingMapCount(size_t host_offset, size_t length) const noexcept {
if (length == 0) {
return 0;
}
if (ahb_bytes == 0) {
return 1;
}
size_t count = 0;
while (length > 0) {
size_t chunk = length;
if (host_offset < ahb_base) {
chunk = (std::min)(chunk, ahb_base - host_offset);
} else if (host_offset < ahb_base + ahb_bytes) {
const size_t local = (host_offset - ahb_base) % ahb_window_size;
chunk = (std::min)(chunk, ahb_window_size - local);
}
host_offset += chunk;
length -= chunk;
++count;
}
return count;
}
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
return ahb_windows;
}
size_t AhbWindowSize() const noexcept {
return ahb_bytes != 0 ? ahb_window_size : 0;
}
size_t AhbBase() const noexcept {
return ahb_base;
}
#endif
~Impl() { ~Impl() {
Release(); Release();
} }
@@ -606,6 +914,12 @@ public:
#ifdef ARCHITECTURE_arm64 #ifdef ARCHITECTURE_arm64
if (True(perms & MemoryPermission::Execute)) if (True(perms & MemoryPermission::Execute))
prot_flags |= PROT_EXEC; prot_flags |= PROT_EXEC;
#endif
#ifdef __ANDROID__
if (ahb_bytes != 0) {
MapBackingRange(virtual_offset, host_offset, length, prot_flags);
return;
}
#endif #endif
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED; int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset); void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
@@ -651,8 +965,18 @@ public:
virtual_base = nullptr; virtual_base = nullptr;
} }
bool IsBackingShared() const noexcept {
#ifdef __ANDROID__
if (ahb_bytes != 0) {
return true;
}
#endif
return fd >= 0;
}
const size_t backing_size; ///< Size of the backing memory in bytes const size_t backing_size; ///< Size of the backing memory in bytes
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
const size_t preferred_offset;
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)}; u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)}; u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
@@ -675,6 +999,21 @@ private:
int ret = close(fd); int ret = close(fd);
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno)); ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
} }
#ifdef __ANDROID__
for (const int window_fd : ahb_fds) {
SyncDmaBufCpuAccess(window_fd, DmaBufSyncEnd);
}
for (AHardwareBuffer* buffer : ahb_windows) {
AHardwareBuffer_release(buffer);
}
ahb_windows.clear();
ahb_fds.clear();
if (ahb_bytes != 0) {
committed_backing_size.store(0, std::memory_order_relaxed);
ahb_bytes = 0;
}
#endif
} }
void AdjustMap(size_t* virtual_offset, size_t* length) { void AdjustMap(size_t* virtual_offset, size_t* length) {
@@ -700,11 +1039,19 @@ private:
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
FreeRegionManager free_manager{}; FreeRegionManager free_manager{};
#ifdef __ANDROID__
std::vector<AHardwareBuffer*> ahb_windows;
std::vector<int> ahb_fds;
size_t ahb_window_size{};
size_t ahb_base{};
size_t ahb_bytes{};
#endif
}; };
#endif // ^^^ POSIX ^^^ #endif // ^^^ POSIX ^^^
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
: backing_size(backing_size_) : backing_size(backing_size_)
, virtual_size(virtual_size_) , virtual_size(virtual_size_)
{ {
@@ -714,7 +1061,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
virtual_base = nullptr; virtual_base = nullptr;
#else #else
// Try to allocate a fastmem arena. // Try to allocate a fastmem arena.
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize); impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize, preferred_offset_);
if (impl->Init()) { if (impl->Init()) {
backing_base = impl->backing_base; backing_base = impl->backing_base;
virtual_base = impl->virtual_base; virtual_base = impl->virtual_base;
@@ -794,6 +1141,46 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
std::memset(backing_base + physical_offset, fill_value, length); std::memset(backing_base + physical_offset, fill_value, length);
} }
std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
#else
return {};
#endif
}
size_t HostMemory::BackingMapCount(size_t host_offset, size_t length) const noexcept {
#ifdef __ANDROID__
return impl ? impl->BackingMapCount(host_offset, length) : (length != 0 ? 1 : 0);
#else
return length != 0 ? 1 : 0;
#endif
}
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindowSize() : 0;
#else
return 0;
#endif
}
bool HostMemory::IsBackingShared() const noexcept {
#if defined(__OPENORBIS__) || defined(__managarm__)
return false;
#else
return impl && impl->IsBackingShared();
#endif
}
size_t HostMemory::BackingHardwareBufferBase() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbBase() : 0;
#else
return 0;
#endif
}
void HostMemory::EnableDirectMappedAddress() { void HostMemory::EnableDirectMappedAddress() {
#if !(defined(__OPENORBIS__) || defined(__managarm__)) #if !(defined(__OPENORBIS__) || defined(__managarm__))
if (impl) { if (impl) {
+20 -1
View File
@@ -8,11 +8,16 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <span>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
struct AHardwareBuffer;
namespace Common { namespace Common {
[[nodiscard]] u64 GetCommittedBackingSize() noexcept;
enum class MemoryPermission : u32 { enum class MemoryPermission : u32 {
Read = 1 << 0, Read = 1 << 0,
Write = 1 << 1, Write = 1 << 1,
@@ -27,7 +32,7 @@ DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
*/ */
class HostMemory { class HostMemory {
public: public:
explicit HostMemory(size_t backing_size_, size_t virtual_size_); explicit HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_ = 0);
~HostMemory(); ~HostMemory();
/** /**
@@ -61,6 +66,20 @@ public:
return backing_base; return backing_base;
} }
[[nodiscard]] size_t BackingSize() const noexcept {
return backing_size;
}
[[nodiscard]] size_t BackingMapCount(size_t host_offset, size_t length) const noexcept;
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
[[nodiscard]] size_t BackingHardwareBufferBase() const noexcept;
[[nodiscard]] bool IsBackingShared() const noexcept;
[[nodiscard]] u8* VirtualBasePointer() noexcept { [[nodiscard]] u8* VirtualBasePointer() noexcept {
return virtual_base; return virtual_base;
} }
+27
View File
@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <type_traits>
namespace Common {
template <class T>
requires(!std::is_array_v<T>)
std::unique_ptr<T> make_unique_for_overwrite() {
return std::unique_ptr<T>(new T);
}
template <class T>
requires std::is_unbounded_array_v<T>
std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
}
template <class T, class... Args>
requires std::is_bounded_array_v<T>
void make_unique_for_overwrite(Args&&...) = delete;
} // namespace Common
+64
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -17,6 +20,10 @@
#endif #endif
#endif #endif
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "common/memory_detect.h" #include "common/memory_detect.h"
namespace Common { namespace Common {
@@ -69,4 +76,61 @@ const MemoryInfo& GetMemInfo() {
return mem_info; return mem_info;
} }
u64 GetPermissibleMapCount() {
constexpr u64 DefaultMapCount = 65530;
constexpr u64 ReservedMaps = 20000;
u64 count = DefaultMapCount;
#ifdef __linux__
if (std::FILE* const file = std::fopen("/proc/sys/vm/max_map_count", "re")) {
char line[32];
if (std::fgets(line, sizeof(line), file) != nullptr) {
const u64 parsed = std::strtoull(line, nullptr, 10);
if (parsed != 0) {
count = parsed;
}
}
std::fclose(file);
}
#endif
if (count <= ReservedMaps) {
return 0;
}
return count - ReservedMaps;
}
u64 GetAvailablePhysicalMemory() {
#ifdef _WIN32
MEMORYSTATUSEX memorystatus;
memorystatus.dwLength = sizeof(memorystatus);
if (GlobalMemoryStatusEx(&memorystatus) == 0) {
return 0;
}
return memorystatus.ullAvailPhys;
#elif defined(__linux__)
static constexpr char AvailableKey[] = "MemAvailable:";
if (std::FILE* const file = std::fopen("/proc/meminfo", "re")) {
char line[256];
u64 available = 0;
while (std::fgets(line, sizeof(line), file) != nullptr) {
if (std::strncmp(line, AvailableKey, sizeof(AvailableKey) - 1) != 0) {
continue;
}
available = std::strtoull(line + sizeof(AvailableKey) - 1, nullptr, 10) * 1024;
break;
}
std::fclose(file);
if (available != 0) {
return available;
}
}
struct sysinfo meminfo;
if (sysinfo(&meminfo) != 0) {
return 0;
}
return static_cast<u64>(meminfo.freeram) * static_cast<u64>(meminfo.mem_unit);
#else
return 0;
#endif
}
} // namespace Common } // namespace Common
+7
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,4 +21,8 @@ struct MemoryInfo {
*/ */
[[nodiscard]] const MemoryInfo& GetMemInfo(); [[nodiscard]] const MemoryInfo& GetMemInfo();
[[nodiscard]] u64 GetPermissibleMapCount();
[[nodiscard]] u64 GetAvailablePhysicalMemory();
} // namespace Common } // namespace Common
+3 -3
View File
@@ -96,15 +96,15 @@ struct PageTable {
} }
/// Write page info atomically /// Write page info atomically
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept { constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer})); data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
} }
inline void MarkRasterizerCached() noexcept { constexpr void MarkRasterizerCached() noexcept {
data_raw.fetch_or(0b111); data_raw.fetch_or(0b111);
} }
inline void MarkDebug(u64 ptr, u16 block) noexcept { constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
Store(true, PageType::DebugMemory, block, ptr); Store(true, PageType::DebugMemory, block, ptr);
} }
+6 -6
View File
@@ -8,7 +8,8 @@
#include <iterator> #include <iterator>
#include <cstring> #include <cstring>
#include <memory>
#include "common/make_unique_for_overwrite.h"
namespace Common { namespace Common {
@@ -37,9 +38,8 @@ public:
ScratchBuffer() = default; ScratchBuffer() = default;
explicit ScratchBuffer(size_type initial_capacity) explicit ScratchBuffer(size_type initial_capacity)
: last_requested_size{initial_capacity} : last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
, buffer_capacity{initial_capacity} buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
, buffer{std::make_unique_for_overwrite<T[]>(initial_capacity)} {}
~ScratchBuffer() = default; ~ScratchBuffer() = default;
ScratchBuffer(const ScratchBuffer&) = delete; ScratchBuffer(const ScratchBuffer&) = delete;
@@ -64,7 +64,7 @@ public:
/// The previously held data will remain intact. /// The previously held data will remain intact.
void resize(size_type size) { void resize(size_type size) {
if (size > buffer_capacity) { if (size > buffer_capacity) {
auto new_buffer = std::make_unique_for_overwrite<T[]>(size); auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T)); std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
buffer = std::move(new_buffer); buffer = std::move(new_buffer);
buffer_capacity = size; buffer_capacity = size;
@@ -77,7 +77,7 @@ public:
void resize_destructive(size_type size) { void resize_destructive(size_type size) {
if (size > buffer_capacity) { if (size > buffer_capacity) {
buffer_capacity = size; buffer_capacity = size;
buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity); buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
} }
last_requested_size = size; last_requested_size = size;
} }
+3
View File
@@ -666,6 +666,9 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks}; Category::RendererHacks};
SwitchableSetting<bool> use_unified_memory{linkage, false, "use_unified_memory",
Category::RendererHacks};
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage, SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
GpuUnswizzleSize::Large, GpuUnswizzleSize::Large,
"gpu_unswizzle_texture_size", "gpu_unswizzle_texture_size",
-15
View File
@@ -96,21 +96,6 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
#ifndef MAP_NOCORE #ifndef MAP_NOCORE
#define MAP_NOCORE 0 #define MAP_NOCORE 0
#endif #endif
#ifndef MADV_FREE
#define MADV_FREE MADV_DONTNEED
#endif
void DecommitVectorPage(uintptr_t base) noexcept {
#if defined(_WIN32)
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
#elif defined(__linux__)
// Linux's MADV_DONTNEED zeros out pages for us
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
#else
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
#endif
}
void* AllocateMemoryPages(std::size_t size) noexcept { void* AllocateMemoryPages(std::size_t size) noexcept {
if (auto page = HostPageSize; size % page != 0) { if (auto page = HostPageSize; size % page != 0) {
+4 -19
View File
@@ -35,12 +35,10 @@ const u64 HostPageMask = ~(HostPageSize - 1);
void* AllocateMemoryPages(std::size_t size) noexcept; void* AllocateMemoryPages(std::size_t size) noexcept;
void FreeMemoryPages(void* base, std::size_t size) noexcept; void FreeMemoryPages(void* base, std::size_t size) noexcept;
void DecommitVectorPage(uintptr_t base) noexcept;
/// A large page-aligned buffer that has optimized memory usage for zero-writes. /// A large page-aligned buffer that has optimized memory usage for zero-writes.
template <typename T> template <typename T>
// MSVC doesn't regard structs with atomics as trivially copyable requires std::is_trivially_copyable_v<T>
// requires std::is_trivially_copyable_v<T>
class SparseLargeVector final { class SparseLargeVector final {
public: public:
constexpr SparseLargeVector() = default; constexpr SparseLargeVector() = default;
@@ -115,7 +113,7 @@ public:
const u64 end_page = AlignUp(base, HostPageSize); const u64 end_page = AlignUp(base, HostPageSize);
const u64 first_size = (std::min)(end_page, end) - base; const u64 first_size = (std::min)(end_page, end) - base;
if (IsCommittedPage(start)) { if (IsCommittedPage(start / sizeof(T))) {
std::memset(reinterpret_cast<void*>(base), 0, first_size); std::memset(reinterpret_cast<void*>(base), 0, first_size);
} }
@@ -125,16 +123,11 @@ public:
base = end_page; base = end_page;
for (u64 page = base; page < end; page += HostPageSize) { for (u64 page = base; page < end; page += HostPageSize) {
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T); if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
if (!IsCommittedPage(index)) {
continue; continue;
} }
if (end - page >= HostPageSize) { std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
DecommitPage(index);
} else {
std::memset(reinterpret_cast<void*>(page), 0, end - page);
}
} }
} }
@@ -189,14 +182,6 @@ private:
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release); committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
} }
constexpr void DecommitPage(std::size_t index) noexcept {
auto page_index = (index * sizeof(T)) >> HostPageBits;
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
DecommitVectorPage(page);
}
std::size_t alloc_size{}; std::size_t alloc_size{};
T* base_ptr{}; T* base_ptr{};
+50
View File
@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _MSC_VER
#include <intrin.h>
#elif defined(ARCHITECTURE_x86_64)
#include <xmmintrin.h>
#endif
#include <atomic>
namespace Common {
/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the
/// supervisor. Should be used on short sequences of code.
struct SpinLock {
SpinLock() noexcept = default;
SpinLock(const SpinLock&) noexcept = delete;
SpinLock& operator=(const SpinLock&) noexcept = delete;
SpinLock(SpinLock&&) noexcept = delete;
SpinLock& operator=(SpinLock&&) noexcept = delete;
inline void lock() noexcept {
while (lck.test_and_set(std::memory_order_acquire)) {
#if defined(ARCHITECTURE_x86_64)
_mm_pause();
#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER)
__yield();
#elif defined(ARCHITECTURE_arm64)
asm("yield");
#endif
}
}
inline void unlock() noexcept {
lck.clear(std::memory_order_release);
}
[[nodiscard]] inline bool try_lock() noexcept {
return !lck.test_and_set(std::memory_order_acquire);
}
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common
+2 -2
View File
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
return uuid; return uuid;
} }
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) { UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
UUID uuid{}; UUID uuid{};
std::memcpy(&uuid.uuid, sha1.data(), sha1.size()); std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F); uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF); uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
return uuid; return uuid;
+1 -1
View File
@@ -104,7 +104,7 @@ struct UUID {
/// @returns A random UUID that is RFC 4122 Version 4 compliant. /// @returns A random UUID that is RFC 4122 Version 4 compliant.
[[nodiscard]] static UUID MakeRandomRFC4122V4(); [[nodiscard]] static UUID MakeRandomRFC4122V4();
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1); [[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
}; };
-40
View File
@@ -1,40 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <cstring>
#include "common/zbic_compression.h"
#define ZSTD_ZBIC_SUPPORT 1
#define ZSTDLIB_VISIBLE static
#define ZSTDLIB_HIDDEN static
#define ZSTDERRORLIB_VISIBLE static
#define ZSTDERRORLIB_HIDDEN static
#undef ZSTD_MULTITHREAD
#include "zstd.h"
#include "zstd.c"
namespace Common::Compression {
bool IsZBIC(std::span<const u8> src) {
if (src.size() < sizeof(u32)) {
return false;
}
u32 magic = 0;
std::memcpy(&magic, src.data(), sizeof(u32));
return magic == ZSTD_MAGICNUMBER; // 0x4349425A ("ZBIC")
}
int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src) {
if (dst.empty() || src.empty()) {
return -1;
}
const size_t res = ZSTD_decompress(dst.data(), dst.size(), src.data(), src.size());
if (ZSTD_isError(res)) {
return -1;
}
return static_cast<int>(res);
}
} // namespace Common::Compression
-15
View File
@@ -1,15 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <span>
#include "common/common_types.h"
namespace Common::Compression {
[[nodiscard]] bool IsZBIC(std::span<const u8> src);
[[nodiscard]] int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src);
} // namespace Common::Compression
-7
View File
@@ -23,8 +23,6 @@ add_library(core STATIC
core_timing.h core_timing.h
cpu_manager.cpp cpu_manager.cpp
cpu_manager.h cpu_manager.h
launch_params.cpp
launch_params.h
crypto/aes_util.cpp crypto/aes_util.cpp
crypto/aes_util.h crypto/aes_util.h
crypto/ctr_encryption_layer.cpp crypto/ctr_encryption_layer.cpp
@@ -798,8 +796,6 @@ add_library(core STATIC
hle/service/ns/application_manager_interface.h hle/service/ns/application_manager_interface.h
hle/service/ns/application_version_interface.cpp hle/service/ns/application_version_interface.cpp
hle/service/ns/application_version_interface.h hle/service/ns/application_version_interface.h
hle/service/ns/async_result.cpp
hle/service/ns/async_result.h
hle/service/ns/content_management_interface.cpp hle/service/ns/content_management_interface.cpp
hle/service/ns/content_management_interface.h hle/service/ns/content_management_interface.h
hle/service/ns/develop_interface.cpp hle/service/ns/develop_interface.cpp
@@ -1205,9 +1201,6 @@ endif()
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS}) target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
if (MSVC)
target_link_libraries(core PRIVATE getopt)
endif()
if (BOOST_NO_HEADERS) if (BOOST_NO_HEADERS)
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc) target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
+36 -20
View File
@@ -22,15 +22,21 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{} {}
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) { u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read); CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
switch (size) { return m_memory.Read8(vaddr);
case sizeof(u64): return m_memory.Read64(vaddr); }
case sizeof(u32): return m_memory.Read32(vaddr); u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
case sizeof(u16): return m_memory.Read16(vaddr); CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
case sizeof(u8): return m_memory.Read8(vaddr); return m_memory.Read16(vaddr);
default: UNREACHABLE(); }
} u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
} }
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) { std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
@@ -44,17 +50,27 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
} }
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) { void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) { if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
switch (size) { m_memory.Write8(vaddr, value);
case sizeof(u64): return m_memory.Write64(vaddr, value);
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
} }
} }
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) { bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) && return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
m_memory.WriteExclusive8(vaddr, value, expected); m_memory.WriteExclusive8(vaddr, value, expected);
@@ -161,7 +177,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>( config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data())); const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK; config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = uint8_t(0); config.page_table_marked_bit = 0;
config.absolute_offset_page_table = true; config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
config.only_detect_misalignment_via_page_table_on_page_boundary = true; config.only_detect_misalignment_via_page_table_on_page_boundary = true;
@@ -177,7 +193,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) { Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries, // Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative. // we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT); config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
} }
} }
+8 -2
View File
@@ -30,12 +30,18 @@ class System;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public: public:
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process); explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
u64 MemoryRead(u32 vaddr, size_t size) override; u8 MemoryRead8(u32 vaddr) override;
u16 MemoryRead16(u32 vaddr) override;
u32 MemoryRead32(u32 vaddr) override;
u64 MemoryRead64(u32 vaddr) override;
std::optional<u32> MemoryReadCode(u32 vaddr) override; std::optional<u32> MemoryReadCode(u32 vaddr) override;
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override; void MemoryWrite8(u32 vaddr, u8 value) override;
void MemoryWrite16(u32 vaddr, u16 value) override;
void MemoryWrite32(u32 vaddr, u32 value) override;
void MemoryWrite64(u32 vaddr, u64 value) override;
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override; bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override; bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override; bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
+35 -21
View File
@@ -10,7 +10,6 @@
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "dynarmic/interface/A64/config.h"
namespace Core { namespace Core {
@@ -22,15 +21,21 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()} , m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{} {}
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) { u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read); CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
switch (size) { return m_memory.Read8(vaddr);
case sizeof(u64): return m_memory.Read64(vaddr); }
case sizeof(u32): return m_memory.Read32(vaddr); u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
case sizeof(u16): return m_memory.Read16(vaddr); CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
case sizeof(u8): return m_memory.Read8(vaddr); return m_memory.Read16(vaddr);
default: UNREACHABLE(); }
} u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
} }
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) { Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read); CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
@@ -48,15 +53,24 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)]; return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
} }
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) { void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) { if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
switch (size) { m_memory.Write8(vaddr, value);
case sizeof(u64): return m_memory.Write64(vaddr, u64(value)); }
case sizeof(u32): return m_memory.Write32(vaddr, u32(value)); }
case sizeof(u16): return m_memory.Write16(vaddr, u16(value)); void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
case sizeof(u8): return m_memory.Write8(vaddr, u8(value)); if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
default: UNREACHABLE(); m_memory.Write16(vaddr, value);
} }
}
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
} }
} }
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) { void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
@@ -202,7 +216,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data())); const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_address_space_bits = std::uint32_t(address_space_bits); config.page_table_address_space_bits = std::uint32_t(address_space_bits);
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK; config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = uint8_t(0); config.page_table_marked_bit = 0;
config.silently_mirror_page_table = false; config.silently_mirror_page_table = false;
config.absolute_offset_page_table = true; config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
@@ -221,7 +235,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) { Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries, // Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative. // we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT); config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
} }
} }
+8 -3
View File
@@ -16,7 +16,6 @@
#include "common/hash.h" #include "common/hash.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" #include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h"
namespace Core::Memory { namespace Core::Memory {
class Memory; class Memory;
@@ -37,13 +36,19 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public: public:
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process); explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
u64 MemoryRead(u64 vaddr, size_t size) override; u8 MemoryRead8(u64 vaddr) override;
u16 MemoryRead16(u64 vaddr) override;
u32 MemoryRead32(u64 vaddr) override;
u64 MemoryRead64(u64 vaddr) override;
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override; Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
std::optional<u32> MemoryReadCode(u64 vaddr) override; std::optional<u32> MemoryReadCode(u64 vaddr) override;
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override; void MemoryWrite8(u64 vaddr, u8 value) override;
void MemoryWrite16(u64 vaddr, u16 value) override;
void MemoryWrite32(u64 vaddr, u32 value) override;
void MemoryWrite64(u64 vaddr, u64 value) override;
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override; void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override; bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override; bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
+5 -1
View File
@@ -120,6 +120,7 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue(); is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
core_timing.SetMulticore(is_multicore); core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); }); core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -147,7 +148,8 @@ struct System::Impl {
!device_memory.has_value() || !device_memory.has_value() ||
is_multicore != Settings::values.use_multi_core.GetValue() || is_multicore != Settings::values.use_multi_core.GetValue() ||
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() != extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
Settings::MemoryLayout::Memory_4Gb); Settings::MemoryLayout::Memory_4Gb) ||
unified_memory != Settings::values.use_unified_memory.GetValue();
if (!must_reinitialize) { if (!must_reinitialize) {
return; return;
@@ -158,6 +160,7 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue(); is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = extended_memory_layout =
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb; Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
Initialize(system); Initialize(system);
} }
@@ -535,6 +538,7 @@ struct System::Impl {
std::atomic_bool is_powered_on{}; std::atomic_bool is_powered_on{};
bool is_multicore : 1 = false; bool is_multicore : 1 = false;
bool extended_memory_layout : 1 = false; bool extended_memory_layout : 1 = false;
bool unified_memory : 1 = false;
bool exit_locked : 1 = false; bool exit_locked : 1 = false;
bool exit_requested : 1 = false; bool exit_requested : 1 = false;
+13 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,9 +15,18 @@ constexpr size_t VirtualReserveSize = 1ULL << 38;
constexpr size_t VirtualReserveSize = 1ULL << 39; constexpr size_t VirtualReserveSize = 1ULL << 39;
#endif #endif
namespace {
size_t ApplicationPoolOffset() {
using Init = Kernel::Board::Nintendo::Nx::KSystemControl::Init;
const size_t dram_size = Init::GetIntendedMemorySize();
const size_t application_pool_size = Init::GetApplicationPoolSize();
return dram_size > application_pool_size ? dram_size - application_pool_size : 0;
}
}
DeviceMemory::DeviceMemory() DeviceMemory::DeviceMemory()
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(), : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
VirtualReserveSize} {} VirtualReserveSize, ApplicationPoolOffset()} {}
DeviceMemory::~DeviceMemory() = default; DeviceMemory::~DeviceMemory() = default;
+35
View File
@@ -20,6 +20,8 @@
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/sparse_large_vector.h" #include "common/sparse_large_vector.h"
struct AHardwareBuffer;
namespace Core { namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL; constexpr size_t DEVICE_PAGEBITS = 12ULL;
@@ -95,6 +97,34 @@ public:
ApplyOpOnPAddr(address, buffer, operation); ApplyOpOnPAddr(address, buffer, operation);
} }
u8* GetPhysicalBase() noexcept {
return reinterpret_cast<u8*>(physical_base);
}
const u8* GetPhysicalBase() const noexcept {
return reinterpret_cast<const u8*>(physical_base);
}
size_t GetPhysicalSize() const noexcept {
return physical_size;
}
std::span<AHardwareBuffer* const> GetBackingHardwareBuffers() const noexcept {
return ahb_windows;
}
size_t GetBackingHardwareBufferWindowSize() const noexcept {
return ahb_window_size;
}
size_t GetBackingHardwareBufferBase() const noexcept {
return ahb_base;
}
bool IsBackingShared() const noexcept {
return backing_is_shared;
}
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const { PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
PAddr subbits = PAddr(address & page_mask); PAddr subbits = PAddr(address & page_mask);
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr; auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
@@ -171,6 +201,11 @@ private:
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl; std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
const uintptr_t physical_base; const uintptr_t physical_base;
const size_t physical_size;
const std::span<AHardwareBuffer* const> ahb_windows;
const size_t ahb_window_size;
const size_t ahb_base;
const bool backing_is_shared;
DeviceInterface* device_inter; DeviceInterface* device_inter;
struct TrackedEntry { struct TrackedEntry {
+5
View File
@@ -171,6 +171,11 @@ struct DeviceMemoryManagerAllocator {
template <typename Traits> template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_) DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())} : physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
, physical_size{device_memory_.buffer.BackingSize()}
, ahb_windows{device_memory_.buffer.BackingHardwareBuffers()}
, ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()}
, ahb_base{device_memory_.buffer.BackingHardwareBufferBase()}
, backing_is_shared{device_memory_.buffer.IsBackingShared()}
, device_inter{nullptr} , device_inter{nullptr}
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS)) , compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS) , tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
+22 -25
View File
@@ -35,20 +35,17 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
} }
static bool IsEOF(IPSFileType type, std::span<const u8> magic) { static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0) return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|| (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0); || (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
} }
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
if (in == nullptr || ips == nullptr) if (in == nullptr || ips == nullptr)
return nullptr; return nullptr;
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
if (type == IPSFileType::Error)
return nullptr;
auto in_data = in->ReadAllBytes(); auto in_data = in->ReadAllBytes();
if (in_data.size() == 0) auto const type = IdentifyMagic(in_data);
if (type == IPSFileType::Error)
return nullptr; return nullptr;
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
@@ -103,7 +100,8 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
struct IPSwitchRecord { struct IPSwitchRecord {
std::vector<uint8_t> data; std::array<uint8_t, 256 - sizeof(size_t)> data;
size_t count;
}; };
struct IPSwitchCompiler::IPSwitchPatch { struct IPSwitchCompiler::IPSwitchPatch {
::Common::unordered_map<u32, IPSwitchRecord> records; ::Common::unordered_map<u32, IPSwitchRecord> records;
@@ -124,23 +122,22 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
IPSwitchRecord r{}; IPSwitchRecord r{};
for (auto it = sv.cbegin(); it < sv.cend(); ) { for (auto it = sv.cbegin(); it < sv.cend(); ) {
if (*it == '\\' && it + 1 < sv.cend()) { if (*it == '\\' && it + 1 < sv.cend()) {
r.data.push_back([it]() { switch (it[1]) {
switch (it[1]) { case 'a': r.data[r.count] = '\a'; break;
case 'a': return '\a'; case 'b': r.data[r.count] = '\b'; break;
case 'b': return '\b'; case 'e': r.data[r.count] = '\e'; break;
case 'e': return '\e'; case 'f': r.data[r.count] = '\f'; break;
case 'f': return '\f'; case 'n': r.data[r.count] = '\n'; break;
case 'n': return '\n'; case 'r': r.data[r.count] = '\r'; break;
case 'r': return '\r'; case 't': r.data[r.count] = '\t'; break;
case 't': return '\t'; case 'v': r.data[r.count] = '\v'; break;
case 'v': return '\v'; case '?': r.data[r.count] = '\?'; break;
case '?': return '\?'; default: r.data[r.count] = it[1]; break;
default: return it[1]; }
} ++r.count;
}());
it += 2; it += 2;
} else { } else {
r.data.push_back(*it); ++r.count;
++it; ++it;
} }
} }
@@ -226,8 +223,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
if (start <= line.cend() && end <= line.cend()) { if (start <= line.cend() && end <= line.cend()) {
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun! // Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
auto const hs = Common::HexStringToVector({start, end}, is_little_endian); auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
r.data.resize(hs.size());
std::memcpy(r.data.data(), hs.data(), hs.size()); std::memcpy(r.data.data(), hs.data(), hs.size());
r.count = hs.size();
LOG_INFO(Loader, "[H] value @ {:#08X}", offset); LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
patches.back().records.insert_or_assign(u32(offset), std::move(r)); patches.back().records.insert_or_assign(u32(offset), std::move(r));
} else { } else {
@@ -296,7 +293,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
if (patch.enabled) { if (patch.enabled) {
for (const auto& record : patch.records) { for (const auto& record : patch.records) {
if (record.first < in_data.size()) { if (record.first < in_data.size()) {
auto replace_size = record.second.data.size(); auto replace_size = record.second.count;
if (record.first + replace_size > in_data.size()) if (record.first + replace_size > in_data.size())
replace_size = in_data.size() - record.first; replace_size = in_data.size() - record.first;
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size); std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
+18 -50
View File
@@ -9,7 +9,6 @@
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include "common/assert.h"
#include "common/hex_util.h" #include "common/hex_util.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
@@ -157,15 +156,12 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
NACP nacp{nacp_file}; NACP nacp{nacp_file};
return nacp.GetVersionString(); return nacp.GetVersionString();
} }
YUZU_NO_INLINE std::optional<u64> GetParentApplicationId(const ContentProvider* provider, u64 title_id) {
return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id);
}
} // Anonymous namespace } // Anonymous namespace
PatchManager::PatchManager(u64 title_id_, PatchManager::PatchManager(u64 title_id_,
const Service::FileSystem::FileSystemController& fs_controller_, const Service::FileSystem::FileSystemController& fs_controller_,
const ContentProvider& content_provider_) const ContentProvider& content_provider_)
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {} : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
PatchManager::~PatchManager() = default; PatchManager::~PatchManager() = default;
@@ -173,35 +169,13 @@ u64 PatchManager::GetTitleID() const {
return title_id; return title_id;
} }
VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const {
const auto get_root = [&](u64 id) {
return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id);
};
auto root = get_root(title_id);
if (!parent_title_id) {
return root;
}
std::vector<VirtualDir> roots{std::move(root), get_root(*parent_title_id)};
std::erase(roots, nullptr);
return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots));
}
std::vector<std::string> PatchManager::GetDisabledAddons() const {
auto disabled = Settings::values.disabled_addons[title_id];
if (parent_title_id) {
const auto& shared = Settings::values.disabled_addons[*parent_title_id];
disabled.insert(disabled.end(), shared.begin(), shared.end());
}
return disabled;
}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr) if (exefs == nullptr)
return exefs; return exefs;
const auto disabled = GetDisabledAddons(); const auto& disabled = Settings::values.disabled_addons[title_id];
bool update_disabled = true; bool update_disabled = true;
std::optional<u32> enabled_version; std::optional<u32> enabled_version;
@@ -329,8 +303,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
} }
// LayeredExeFS // LayeredExeFS
const auto load_dir = GetModificationLoadRoot(); const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = GetModificationLoadRoot(true); const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
if (load_dir != nullptr) { if (load_dir != nullptr) {
@@ -372,7 +346,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
} }
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const { std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
const auto disabled = GetDisabledAddons(); const auto& disabled = Settings::values.disabled_addons[title_id];
const auto nso_build_id = fmt::format("{:0<64}", build_id); const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out; std::vector<VirtualFile> out;
@@ -431,7 +405,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir = GetModificationLoadRoot(); const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) { if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso; return nso;
@@ -474,7 +448,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name); LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
const auto load_dir = GetModificationLoadRoot(); const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) { if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false; return false;
@@ -488,13 +462,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
} }
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const { std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
const auto load_dir = GetModificationLoadRoot(); const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) { if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {}; return {};
} }
const auto disabled = GetDisabledAddons(); const auto& disabled = Settings::values.disabled_addons[title_id];
auto patch_dirs = load_dir->GetSubdirectories(); auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); }); std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
@@ -528,16 +502,17 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
return out; return out;
} }
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const { static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
const auto load_dir = GetModificationLoadRoot(); const Service::FileSystem::FileSystemController& fs_controller) {
const auto sdmc_load_dir = GetModificationLoadRoot(true); const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data && if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
type != ContentRecordType::HtmlDocument) || type != ContentRecordType::HtmlDocument) ||
(load_dir == nullptr && sdmc_load_dir == nullptr)) { (load_dir == nullptr && sdmc_load_dir == nullptr)) {
return; return;
} }
const auto disabled = GetDisabledAddons(); const auto& disabled = Settings::values.disabled_addons[title_id];
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories(); std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) { if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
patch_dirs.push_back(sdmc_load_dir); patch_dirs.push_back(sdmc_load_dir);
@@ -616,7 +591,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// Game Updates // Game Updates
const auto update_tid = GetUpdateTitleID(title_id); const auto update_tid = GetUpdateTitleID(title_id);
const auto disabled = GetDisabledAddons(); const auto& disabled = Settings::values.disabled_addons[title_id];
bool update_disabled = true; bool update_disabled = true;
std::optional<u32> enabled_version; std::optional<u32> enabled_version;
@@ -723,7 +698,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// LayeredFS // LayeredFS
if (apply_layeredfs) { if (apply_layeredfs) {
ApplyLayeredFS(romfs, type); ApplyLayeredFS(romfs, title_id, type, fs_controller);
} }
return romfs; return romfs;
@@ -1097,22 +1072,15 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::optional<u32> PatchManager::GetGameVersion() const { std::optional<u32> PatchManager::GetGameVersion() const {
const auto update_tid = GetUpdateTitleID(title_id); const auto update_tid = GetUpdateTitleID(title_id);
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) { if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
const auto version = content_provider.GetEntryVersion(update_tid); return content_provider.GetEntryVersion(update_tid);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
} }
const auto version = content_provider.GetEntryVersion(title_id); return content_provider.GetEntryVersion(title_id);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
} }
PatchManager::Metadata PatchManager::GetControlMetadata() const { PatchManager::Metadata PatchManager::GetControlMetadata() const {
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control); const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr) { if (base_control_nca == nullptr) {
if (parent_title_id) {
const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id);
const PatchManager parent{control_id, fs_controller, content_provider};
return parent.GetControlMetadata();
}
return {}; return {};
} }
-4
View File
@@ -109,14 +109,10 @@ public:
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept; [[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
private: private:
[[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const;
[[nodiscard]] std::vector<std::string> GetDisabledAddons() const;
void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const;
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const; const std::string& build_id) const;
u64 title_id; u64 title_id;
std::optional<u64> parent_title_id;
const Service::FileSystem::FileSystemController& fs_controller; const Service::FileSystem::FileSystemController& fs_controller;
const ContentProvider& content_provider; const ContentProvider& content_provider;
}; };
-17
View File
@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <limits>
#include <random> #include <random>
#include <regex> #include <regex>
#include <openssl/evp.h> #include <openssl/evp.h>
@@ -349,22 +348,6 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
} }
std::optional<u64> ContentProvider::GetParentApplicationId(u64 program_id) const {
const auto application_id = GetBaseTitleID(program_id);
const auto program_index = program_id - application_id;
if (program_index == 0 || program_index > std::numeric_limits<u8>::max()) {
return std::nullopt;
}
if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) {
return std::nullopt;
}
if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program))
|| (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) {
return application_id;
}
return std::nullopt;
}
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {} PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
bool PlaceholderCache::Create(const NcaID& id, u64 size) const { bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
-2
View File
@@ -100,8 +100,6 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const = 0; std::optional<u64> title_id = {}) const = 0;
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
protected: protected:
// A single instance of KeyManager to be used by GetEntry() // A single instance of KeyManager to be used by GetEntry()
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
-3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
+4 -3
View File
@@ -109,7 +109,8 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
auto reference = std::make_unique<FileReference>(); auto reference = std::make_unique<FileReference>();
this->InsertReferenceIntoListLocked(*reference); this->InsertReferenceIntoListLocked(*reference);
auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path)); auto file = std::shared_ptr<RealVfsFile>(
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
cache[path] = file; cache[path] = file;
return file; return file;
@@ -176,7 +177,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
return std::make_shared<RealVfsDirectory>(*this, path, perms); return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
} }
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
@@ -184,7 +185,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
if (!FS::CreateDirs(path)) { if (!FS::CreateDirs(path)) {
return nullptr; return nullptr;
} }
return std::make_shared<RealVfsDirectory>(*this, path, perms); return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
} }
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
+6 -5
View File
@@ -82,9 +82,6 @@ class RealVfsFile : public VfsFile {
friend class RealVfsFilesystem; friend class RealVfsFilesystem;
public: public:
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, OpenMode perms = OpenMode::Read,
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
~RealVfsFile() override; ~RealVfsFile() override;
std::string GetName() const override; std::string GetName() const override;
@@ -98,6 +95,9 @@ public:
bool Rename(std::string_view name) override; bool Rename(std::string_view name) override;
private: private:
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
const std::string& path, OpenMode perms = OpenMode::Read,
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
RealVfsFilesystem& base; RealVfsFilesystem& base;
std::unique_ptr<FileReference> reference; std::unique_ptr<FileReference> reference;
@@ -113,8 +113,6 @@ class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem; friend class RealVfsFilesystem;
public: public:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
OpenMode perms = OpenMode::Read);
~RealVfsDirectory() override; ~RealVfsDirectory() override;
VirtualFile GetFileRelative(std::string_view relative_path) const override; VirtualFile GetFileRelative(std::string_view relative_path) const override;
@@ -140,6 +138,9 @@ public:
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
private: private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
OpenMode perms = OpenMode::Read);
template <typename T, typename R> template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const; std::vector<std::shared_ptr<R>> IterateEntries() const;
+8 -25
View File
@@ -12,7 +12,6 @@
#include "hid_core/frontend/emulated_controller.h" #include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h" #include "hid_core/hid_core.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include <array>
namespace Core::Frontend { namespace Core::Frontend {
@@ -30,39 +29,23 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
const std::size_t min_supported_players = const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players; parameters.enable_single_mode ? 1 : parameters.min_players;
using Core::HID::NpadStyleIndex;
const std::size_t max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
std::size_t num_selected_players = 0;
std::array<bool, HID::HIDCore::available_controllers> keep_connected{};
// reserve existing AND valid players before filling slots. include Handheld, but not other // Disconnect Handheld first.
for (std::size_t index = 0; index < hid_core.available_controllers - 1; ++index) {
const auto* controller = hid_core.GetEmulatedControllerByIndex(index);
if (!parameters.keep_controllers_connected || !controller->IsConnected() || num_selected_players >= max_supported_players) continue;
const auto style = controller->GetNpadStyleIndex();
keep_connected[index] =
(style == NpadStyleIndex::Fullkey && parameters.allow_pro_controller) ||
(style == NpadStyleIndex::JoyconDual && parameters.allow_dual_joycons) ||
(style == NpadStyleIndex::JoyconLeft && parameters.allow_left_joycon) ||
(style == NpadStyleIndex::JoyconRight && parameters.allow_right_joycon) ||
(style == NpadStyleIndex::Handheld && parameters.enable_single_mode && parameters.allow_handheld && !Settings::IsDockedMode()) ||
(style == NpadStyleIndex::GameCube && parameters.allow_gamecube_controller);
num_selected_players += keep_connected[index];
}
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect(); handheld->Disconnect();
// Deduce the best configuration based on the input parameters. // Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) { for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
auto* controller = hid_core.GetEmulatedControllerByIndex(index); auto* controller = hid_core.GetEmulatedControllerByIndex(index);
if (keep_connected[index]) continue; // First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
controller->Disconnect(); controller->Disconnect();
// only add players still needed to reach the minimum // Only connect the minimum number of required players.
if (num_selected_players >= min_supported_players) continue; if (index >= min_supported_players) {
++num_selected_players; continue;
}
// Connect controllers based on the following priority list from highest to lowest priority: // Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
-8
View File
@@ -465,10 +465,6 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
// Check if we need scheduling. If we do, then we can't complete the switch and should // Check if we need scheduling. If we do, then we can't complete the switch and should
// retry. // retry.
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) { if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// Our switch failed. // Our switch failed.
// We should unlock the thread context, and then retry. // We should unlock the thread context, and then retry.
highest_priority_thread->m_context_guard.unlock(); highest_priority_thread->m_context_guard.unlock();
@@ -500,10 +496,6 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
// Check if the thread is terminated by checking the DPC flags. // Check if the thread is terminated by checking the DPC flags.
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) { if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// The thread isn't terminated, so we want to unlock it. // The thread isn't terminated, so we want to unlock it.
thread->m_context_guard.unlock(); thread->m_context_guard.unlock();
} }
+4 -3
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -12,6 +12,7 @@
#include "common/atomic_ops.h" #include "common/atomic_ops.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/spin_lock.h"
namespace Kernel { namespace Kernel {
@@ -29,7 +30,7 @@ public:
}; };
public: public:
KSlabHeapImpl() = default; constexpr KSlabHeapImpl() = default;
void Initialize() { void Initialize() {
ASSERT(m_head == nullptr); ASSERT(m_head == nullptr);
@@ -67,7 +68,7 @@ public:
private: private:
std::atomic<Node*> m_head{}; std::atomic<Node*> m_head{};
std::mutex m_lock; Common::SpinLock m_lock;
}; };
} // namespace impl } // namespace impl
+2 -1
View File
@@ -19,6 +19,7 @@
#include "common/intrusive_red_black_tree.h" #include "common/intrusive_red_black_tree.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/spin_lock.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h" #include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_light_lock.h"
@@ -919,7 +920,7 @@ private:
bool m_resource_limit_release_hint{}; bool m_resource_limit_release_hint{};
bool m_is_kernel_address_key{}; bool m_is_kernel_address_key{};
StackParameters m_stack_parameters{}; StackParameters m_stack_parameters{};
std::mutex m_context_guard{}; Common::SpinLock m_context_guard{};
// For emulation // For emulation
std::shared_ptr<Common::Fiber> m_host_context{}; std::shared_ptr<Common::Fiber> m_host_context{};
@@ -23,12 +23,6 @@ constexpr std::size_t profile_username_size{32};
using ProfileUsername = std::array<u8, profile_username_size>; using ProfileUsername = std::array<u8, profile_username_size>;
using UserIDArray = std::array<Common::UUID, MAX_USERS>; using UserIDArray = std::array<Common::UUID, MAX_USERS>;
// This is nn::account::Uid
struct Uid {
std::array<u8, 0x10> unk0;
};
static_assert(sizeof(Uid) == 0x10);
/// Contains extra data related to a user. /// Contains extra data related to a user.
/// TODO: RE this structure /// TODO: RE this structure
struct UserData { struct UserData {
+4 -11
View File
@@ -29,7 +29,6 @@
#include "core/hle/service/am/frontend/applet_web_browser.h" #include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/service/storage.h" #include "core/hle/service/am/service/storage.h"
#include "core/hle/service/am/window_system.h"
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
namespace Service::AM::Frontend { namespace Service::AM::Frontend {
@@ -73,16 +72,10 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
void FrontendApplet::Exit() { void FrontendApplet::Exit() {
auto applet_ = applet.lock(); auto applet_ = applet.lock();
{
std::scoped_lock lk{applet_->lock}; std::scoped_lock lk{applet_->lock};
applet_->is_completed = true; applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel()); applet_->state_changed_event.Signal(system.Kernel());
}
if (auto caller_applet = applet_->caller_applet.lock()) {
std::scoped_lock lk{caller_applet->lock};
std::erase(caller_applet->child_applets, applet_);
}
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate();
} }
FrontendAppletSet::FrontendAppletSet() = default; FrontendAppletSet::FrontendAppletSet() = default;
@@ -92,13 +92,6 @@ public:
} }
} }
void RequestFocusStateChangedNotification(Kernel::KernelCore& kernel) {
if (m_focus_state_changed_notification_enabled) {
m_has_focus_state_changed = true;
this->SignalSystemEventIfNeeded(kernel);
}
}
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel); void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
public: public:
@@ -347,22 +347,21 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) { Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
LOG_WARNING(Service_AM, "(stubbed)"); LOG_WARNING(Service_AM, "(stubbed)");
R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown); R_UNLESS(out_pseudo_device_id, ResultUnknown);
// This should be hashed with the device specific hash // This should be hashed with the device specific hash
// for now this will do // for now this will do
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id); const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
R_UNLESS(res.first != nullptr, ResultUnknown); u8 hash[EVP_MAX_MD_SIZE];
std::array<u8, EVP_MAX_MD_SIZE> hash;
unsigned int hash_len = 0; unsigned int hash_len = 0;
auto const seed = res.first->raw.seed_for_pseudo_device_id; auto const seed = res.first->raw.seed_for_pseudo_device_id;
EVP_MD_CTX *ctx = EVP_MD_CTX_new(); EVP_MD_CTX *ctx = EVP_MD_CTX_new();
auto const algorithm = EVP_sha1(); auto const algorithm = EVP_sha1();
EVP_DigestInit_ex(ctx, algorithm, nullptr); EVP_DigestInit_ex(ctx, algorithm, nullptr);
EVP_DigestUpdate(ctx, &seed, sizeof(seed)); EVP_DigestUpdate(ctx, &seed, sizeof(seed));
EVP_DigestFinal_ex(ctx, hash.data(), &hash_len); EVP_DigestFinal_ex(ctx, hash, &hash_len);
EVP_MD_CTX_free(ctx); EVP_MD_CTX_free(ctx);
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16}); *out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
R_SUCCEED(); R_SUCCEED();
} }
@@ -164,15 +164,15 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) { Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) { if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
std::scoped_lock lk{caller_applet->lock}; caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel());
const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState(); caller_applet->lifecycle_manager.RequestResumeNotification();
const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication(); caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel());
if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel()); caller_applet->lifecycle_manager.UpdateRequestedFocusState();
else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel());
} }
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) { if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
auto impl = (*out_storage)->GetImpl(); auto impl = (*out_storage)->GetImpl();
@@ -122,10 +122,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
auto broker = std::make_shared<AppletDataBroker>(system); auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet; applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker; applet->caller_applet_broker = broker;
{ caller_applet->child_applets.push_back(applet);
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet);
}
window_system.TrackApplet(applet, false); window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet); return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
} }
@@ -151,10 +148,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
applet->caller_applet = caller_applet; applet->caller_applet = caller_applet;
applet->caller_applet_broker = storage; applet->caller_applet_broker = storage;
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
{ caller_applet->child_applets.push_back(applet);
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet); window_system.TrackApplet(applet, false);
}
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet); return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
} }
+1 -1
View File
@@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity. // Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
s32 z_index = Background; s32 z_index = Background;
if (is_overlay) { if (is_overlay) {
z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background; z_index = Overlay;
} else if (inherited_foreground) { } else if (inherited_foreground) {
z_index = is_obscured ? Foreground : ForegroundVisible; z_index = is_obscured ? Foreground : ForegroundVisible;
} }
+1 -1
View File
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30); server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30); server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
@@ -154,9 +154,6 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{720, nullptr, "AbandonAccessFailure"}, {720, nullptr, "AbandonAccessFailure"},
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
{810, nullptr, "RegisterProgramIndexMapInfo"}, {810, nullptr, "RegisterProgramIndexMapInfo"},
{820, nullptr, "GetContentStorageInfoIndex"},
{830, nullptr, "EncryptStreamPlaySaveData"},
{831, nullptr, "DecryptStreamPlaySaveData"},
{1000, nullptr, "SetBisRootForHost"}, {1000, nullptr, "SetBisRootForHost"},
{1001, nullptr, "SetSaveDataSize"}, {1001, nullptr, "SetSaveDataSize"},
{1002, nullptr, "SetSaveDataRootPath"}, {1002, nullptr, "SetSaveDataRootPath"},
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{547, nullptr, "GetAllowedBluetoothLinksCount"}, {547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"}, {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"}, {549, nullptr, "GetConnectableRegisteredDevices"},
{551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least) {551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
{700, nullptr, "ActivateUniquePad"}, {700, nullptr, "ActivateUniquePad"},
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"}, {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"}, {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
} }
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) { void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551 LOG_WARNING(Service_HID, "(STUBBED) called");
struct RegisterData { struct RegisterData {
std::array<u8, 0x68> data; std::array<u8, 0x68> data;
-1
View File
@@ -22,7 +22,6 @@
namespace IPC { namespace IPC {
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301}; constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
struct ResponseBuilder { struct ResponseBuilder {
+34 -28
View File
@@ -71,14 +71,17 @@ public:
void InstructionSynchronizationBarrierRaised() override { void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch last_code_addr = u64(-1); //reset back, force refetch
} }
u64 MemoryRead(u64 vaddr, size_t size) override { u8 MemoryRead8(u64 vaddr) override {
switch (size) { return ReadMemory<u8>(vaddr);
case sizeof(u64): return ReadMemory<u64>(vaddr); }
case sizeof(u32): return ReadMemory<u32>(vaddr); u16 MemoryRead16(u64 vaddr) override {
case sizeof(u16): return ReadMemory<u16>(vaddr); return ReadMemory<u16>(vaddr);
case sizeof(u8): return ReadMemory<u8>(vaddr); }
default: UNREACHABLE(); u32 MemoryRead32(u64 vaddr) override {
} return ReadMemory<u32>(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
return ReadMemory<u64>(vaddr);
} }
u128 MemoryRead128(u64 vaddr) override { u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(vaddr); return ReadMemory<u128>(vaddr);
@@ -86,19 +89,22 @@ public:
std::string MemoryReadCString(u64 vaddr) { std::string MemoryReadCString(u64 vaddr) {
std::string result{}; std::string result{};
u8 next; u8 next;
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0) while ((next = MemoryRead8(vaddr++)) != 0)
result += char(next); result += char(next);
return result; return result;
} }
void MemoryWrite(u64 vaddr, u64 value, size_t size) override { void MemoryWrite8(u64 vaddr, u8 value) override {
switch (size) { WriteMemory<u8>(vaddr, value);
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break; }
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break; void MemoryWrite16(u64 vaddr, u16 value) override {
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break; WriteMemory<u16>(vaddr, value);
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break; }
default: UNREACHABLE(); void MemoryWrite32(u64 vaddr, u32 value) override {
} WriteMemory<u32>(vaddr, value);
}
void MemoryWrite64(u64 vaddr, u64 value) override {
WriteMemory<u64>(vaddr, value);
} }
void MemoryWrite128(u64 vaddr, u128 value) override { void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(vaddr, value); WriteMemory<u128>(vaddr, value);
@@ -187,14 +193,14 @@ public:
// The loaded NRO file has ELF relocations that must be processed before it can run. // The loaded NRO file has ELF relocations that must be processed before it can run.
// Normally this would be processed by RTLD, but in HLE context, we don't have // Normally this would be processed by RTLD, but in HLE context, we don't have
// the linker available, so we have to do it ourselves. // the linker available, so we have to do it ourselves.
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))}; const VAddr mod_offset{callbacks->MemoryRead32(4)};
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0')) if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
return false; return false;
// For more info about dynamic entries, see the ELF ABI specification: // For more info about dynamic entries, see the ELF ABI specification:
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))}; VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
VAddr rela_dyn = 0, relr_dyn = 0; VAddr rela_dyn = 0, relr_dyn = 0;
size_t num_rela = 0, num_relr = 0; size_t num_rela = 0, num_relr = 0;
while (true) { while (true) {
@@ -216,8 +222,8 @@ public:
for (size_t i = 0; i < num_rela; i++) { for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) { if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))}; const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64)); callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
} }
} }
@@ -225,7 +231,7 @@ public:
for (size_t i = 0; i < num_relr; i++) { for (size_t i = 0; i < num_relr; i++) {
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr)); const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
const auto incr = [&](VAddr where) { const auto incr = [&](VAddr where) {
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64)); callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
}; };
if ((relr & 1) == 0) { if ((relr & 1) == 0) {
// where pointer // where pointer
@@ -288,7 +294,7 @@ public:
if (argument_stack.size() > 8) { if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN); const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
for (size_t i = 8; i < argument_stack.size(); i++) for (size_t i = 8; i < argument_stack.size(); i++)
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64)); callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
jit->SetSP(new_sp); jit->SetSP(new_sp);
} }
// Reset the call state for the next invocation // Reset the call state for the next invocation
@@ -379,17 +385,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
if (dest < src) { if (dest < src) {
for (size_t i = 0; i < n; i++) for (size_t i = 0; i < n; i++)
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8)); MemoryWrite8(dest + i, MemoryRead8(src + i));
} else { } else {
for (size_t i = n; i > 0; i--) for (size_t i = n; i > 0; i--)
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8)); MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
} }
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) { } else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
const VAddr dest{parent.jit->GetRegister(0)}; const VAddr dest{parent.jit->GetRegister(0)};
const u64 c{parent.jit->GetRegister(1)}; const u64 c{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)}; const size_t n{parent.jit->GetRegister(2)};
for (size_t i = 0; i < n; i++) for (size_t i = 0; i < n; i++)
MemoryWrite(dest + i, u8(c), sizeof(u8)); MemoryWrite8(dest + i, u8(c));
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) { } else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
// X0 contains a char* for a symbol to resolve // X0 contains a char* for a symbol to resolve
const auto name{MemoryReadCString(parent.jit->GetRegister(0))}; const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
@@ -416,7 +422,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
} }
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) { void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
auto const inst = MemoryRead(pc, sizeof(u32)); auto const inst = MemoryRead32(pc);
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst); LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
parent.jit->HaltExecution(); parent.jit->HaltExecution();
} }
-117
View File
@@ -6,16 +6,10 @@
#include "common/string_util.h" #include "common/string_util.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/result.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ngc/ngc.h" #include "core/hle/service/ngc/ngc.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
#include "frontend_common/firmware_manager.h"
namespace Service::NGC { namespace Service::NGC {
@@ -172,123 +166,12 @@ public:
} }
}; };
struct SaveDataHandle {
u64 unk0;
};
static_assert(sizeof(SaveDataHandle) == 0x08);
class IUserShimScopedObject final : public ServiceFramework<IUserShimScopedObject> {
public:
explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{450, nullptr, "InitializeForSaveData"},
{451, nullptr, "FinalizeForSaveData"},
{452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"},
{453, nullptr, "CloseSaveData"},
{454, D<&IUserShimScopedObject::ReadSaveSlot>, "ReadSaveSlot"},
{455, D<&IUserShimScopedObject::WriteSaveSlot>, "WriteSaveSlot"},
{456, nullptr, "FlushSaveSlot"},
{457, nullptr, "CommitSaveData"},
};
// clang-format on
RegisterHandlers(functions);
}
Result OpenSaveData(Account::Uid unk0, Out<SaveDataHandle> unk1) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result ReadSaveSlot(s32 offset, SaveDataHandle handle, OutBuffer<BufferAttr_HipcAutoSelect> out_data, Out<u32> out_size) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result WriteSaveSlot(s32 offset, SaveDataHandle handle, InBuffer<BufferAttr_HipcAutoSelect> out_data) {
LOG_WARNING(Service_NGC, "stubbed");
// to implement
R_SUCCEED();
}
};
class IUserService final : public ServiceFramework<IUserService> {
public:
explicit IUserService(Core::System& system_) : ServiceFramework(system_, "stpl:u") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&IUserService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(u32 unk0, OutInterface<IUserShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<IUserShimScopedObject>(system);
R_SUCCEED();
}
};
class ISystemShimScopedObject final : public ServiceFramework<ISystemShimScopedObject> {
public:
explicit ISystemShimScopedObject(Core::System& system_) : ServiceFramework(system_, "ISystemShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{106, nullptr, "Cmd106"},
{107, nullptr, "Cmd107"},
{108, D<&ISystemShimScopedObject::Cmd108>, "Cmd108"},
{207, nullptr, "Cmd207"},
{208, D<&ISystemShimScopedObject::Cmd208>, "Cmd208"},
{209, nullptr, "Cmd209"},
{210, nullptr, "Cmd210"},
{211, nullptr, "Cmd211"},
{212, nullptr, "Cmd212"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd108() {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result Cmd208(Out<std::array<u8, 0x20>> unk0) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
};
class ISystemService final : public ServiceFramework<ISystemService> {
public:
explicit ISystemService(Core::System& system_) : ServiceFramework(system_, "stpl:sys") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&ISystemService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(OutInterface<ISystemShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<ISystemShimScopedObject>(system);
R_SUCCEED();
}
};
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4); server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4); server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4); server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
// +23.0.0
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 23) {
server_manager->RegisterNamedService("stpl:u", std::make_shared<IUserService>(system), 4);
server_manager->RegisterNamedService("stpl:sys", std::make_shared<ISystemService>(system), 4);
}
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
@@ -228,9 +228,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{930, nullptr, "Unknown930"}, //20.0.0+ {930, nullptr, "Unknown930"}, //20.0.0+
{931, nullptr, "Unknown931"}, //20.0.0+ {931, nullptr, "Unknown931"}, //20.0.0+
{933, nullptr, "Unknown933"}, //20.0.0+ {933, nullptr, "Unknown933"}, //20.0.0+
{934, nullptr, "Unknown934"}, //21.0.0+ {934, nullptr, "Unknown934"}, //20.0.0+
{935, nullptr, "Unknown935"}, //21.0.0+ {935, nullptr, "Unknown935"}, //20.0.0+
{936, D<&IApplicationManagerInterface::Unknown936>, "Unknown936"}, //21.0.0+ {936, nullptr, "Unknown936"}, //20.0.0+
{1000, nullptr, "RequestVerifyApplicationDeprecated"}, {1000, nullptr, "RequestVerifyApplicationDeprecated"},
{1001, nullptr, "CorruptApplicationForDebug"}, {1001, nullptr, "CorruptApplicationForDebug"},
{1002, nullptr, "RequestVerifyAddOnContentsRights"}, {1002, nullptr, "RequestVerifyAddOnContentsRights"},
@@ -422,7 +422,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{4039, nullptr, "Unknown4039"}, //20.0.0+ {4039, nullptr, "Unknown4039"}, //20.0.0+
{4040, nullptr, "Unknown4040"}, //20.0.0+ {4040, nullptr, "Unknown4040"}, //20.0.0+
{4041, nullptr, "Unknown4041"}, //20.0.0+ {4041, nullptr, "Unknown4041"}, //20.0.0+
{4042, D<&IApplicationManagerInterface::Unknown4042>, "Unknown4042"}, //20.0.0+ {4042, nullptr, "Unknown4042"}, //20.0.0+
{4043, nullptr, "Unknown4043"}, //20.0.0+ {4043, nullptr, "Unknown4043"}, //20.0.0+
{4044, nullptr, "Unknown4044"}, //20.0.0+ {4044, nullptr, "Unknown4044"}, //20.0.0+
{4045, nullptr, "Unknown4045"}, //20.0.0+ {4045, nullptr, "Unknown4045"}, //20.0.0+
@@ -639,12 +639,6 @@ Result IApplicationManagerInterface::IsGameCardApplicationRunning(Out<bool> out_
R_SUCCEED(); R_SUCCEED();
} }
Result IApplicationManagerInterface::Unknown936(Out<u64> out_result) {
LOG_WARNING(Service_NS, "(STUBBED) called.");
*out_result = 0;
R_SUCCEED();
}
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled( Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
Out<bool> out_is_any_application_entity_installed) { Out<bool> out_is_any_application_entity_installed) {
LOG_WARNING(Service_NS, "(STUBBED) called"); LOG_WARNING(Service_NS, "(STUBBED) called");
@@ -868,15 +862,6 @@ Result IApplicationManagerInterface::Unknown4023(Out<u64> out_result) {
R_SUCCEED(); R_SUCCEED();
} }
Result IApplicationManagerInterface::Unknown4042(OutInterface<IAsyncResult> out_interface,
OutCopyHandle<Kernel::KReadableEvent> out_event,
u64 arg1, u64 arg2) {
LOG_WARNING(Service_NS, "(STUBBED) called, arg1={:016X}, arg2={:016X}", arg1, arg2);
*out_event = unknown_event.GetHandle();
*out_interface = std::make_shared<IAsyncResult>(system, &unknown_event);
R_SUCCEED();
}
Result IApplicationManagerInterface::Unknown4053() { Result IApplicationManagerInterface::Unknown4053() {
LOG_WARNING(Service_NS, "(STUBBED) called."); LOG_WARNING(Service_NS, "(STUBBED) called.");
R_SUCCEED(); R_SUCCEED();
@@ -7,7 +7,6 @@
#pragma once #pragma once
#include "core/hle/service/cmif_types.h" #include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/async_result.h"
#include "core/hle/service/ns/language.h" #include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h" #include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/os/event.h" #include "core/hle/service/os/event.h"
@@ -35,7 +34,6 @@ public:
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetGameCardWakenReadyEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetGameCardWakenReadyEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result IsGameCardApplicationRunning(Out<bool> out_is_running); Result IsGameCardApplicationRunning(Out<bool> out_is_running);
Result Unknown936(Out<u64> out_result);
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed); Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
Result GetApplicationViewDeprecated( Result GetApplicationViewDeprecated(
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views, OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
@@ -73,9 +71,6 @@ public:
InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer); InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer);
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event); Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result Unknown4023(Out<u64> out_result); Result Unknown4023(Out<u64> out_result);
Result Unknown4042(OutInterface<IAsyncResult> out_interface,
OutCopyHandle<Kernel::KReadableEvent> out_event,
u64 arg1, u64 arg2);
Result Unknown4053(); Result Unknown4053();
Result Unknown4105(); Result Unknown4105();

Some files were not shown because too many files have changed in this diff Show More