mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-27 19:26:52 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d336341c8d | |||
| a4fd5c6b0b | |||
| ef7e9c00b0 | |||
| 3be0db1266 | |||
| 815325ccec | |||
| 37fe911952 | |||
| f273423b2b | |||
| 87d2f03c39 | |||
| bebc19da32 | |||
| 99bf8cf51a | |||
| dbeb73ee01 | |||
| cb73a4dcc7 | |||
| 278f411dad | |||
| 3d37a816c6 | |||
| a25e32676f | |||
| 38df54edfe | |||
| 3266c20e3f | |||
| 9cd24e85c2 | |||
| 74b5e10dc5 | |||
| a277b62fe4 | |||
| 908b1e9a37 | |||
| 847e91c3a8 | |||
| 2d1eb0dab9 | |||
| 9a30172e45 | |||
| 07f40d5cac | |||
| 3c38afad74 | |||
| 0ce29be608 |
@@ -1,34 +0,0 @@
|
|||||||
diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
|
|
||||||
index 2859ee1..766961f 100644
|
|
||||||
--- a/libavutil/hwcontext_vulkan.c
|
|
||||||
+++ b/libavutil/hwcontext_vulkan.c
|
|
||||||
@@ -1688,10 +1688,12 @@ static int setup_queue_families(AVHWDeviceContext *ctx, VkDeviceCreateInfo *cd)
|
|
||||||
|
|
||||||
hwctx->nb_qf = 0;
|
|
||||||
hwctx->queue_flags = 0;
|
|
||||||
+/* SD gamemode vkroots crash
|
|
||||||
#ifdef VK_KHR_internally_synchronized_queues
|
|
||||||
if (p->vkctx.extensions & FF_VK_EXT_INTERNAL_QUEUE_SYNC)
|
|
||||||
hwctx->queue_flags |= VK_DEVICE_QUEUE_CREATE_INTERNALLY_SYNCHRONIZED_BIT_KHR;
|
|
||||||
#endif
|
|
||||||
+*/
|
|
||||||
|
|
||||||
/* Pick each queue family to use. */
|
|
||||||
#define PICK_QF(type, vid_op) \
|
|
||||||
diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c
|
|
||||||
index b2e575c..5b3c9cf 100644
|
|
||||||
--- a/libavutil/vulkan.c
|
|
||||||
+++ b/libavutil/vulkan.c
|
|
||||||
@@ -574,10 +574,12 @@ int ff_vk_exec_pool_init(FFVulkanContext *s, AVVulkanDeviceQueueFamily *qf,
|
|
||||||
e->qf = qf->idx;
|
|
||||||
VkDeviceQueueInfo2 qinfo = {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2,
|
|
||||||
+/* SD gamemode vkroots crash
|
|
||||||
#ifdef VK_KHR_internally_synchronized_queues
|
|
||||||
.flags = internal_queue_sync ?
|
|
||||||
VK_DEVICE_QUEUE_CREATE_INTERNALLY_SYNCHRONIZED_BIT_KHR : 0,
|
|
||||||
#endif
|
|
||||||
+*/
|
|
||||||
.queueFamilyIndex = qf->idx,
|
|
||||||
.queueIndex = e->qi,
|
|
||||||
};
|
|
||||||
+7
-2
@@ -70,9 +70,9 @@
|
|||||||
"version": "v1.3.18"
|
"version": "v1.3.18"
|
||||||
},
|
},
|
||||||
"ffmpeg": {
|
"ffmpeg": {
|
||||||
"hash": "68f46abf0d5dc47ab95083d59e273131a8df7948b82e62db00f00fa416e1d0bd24b799671b5a60752209bb2ac0f75295055ed0085c89a98113323cde23b69710",
|
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
||||||
"repo": "FFmpeg/FFmpeg",
|
"repo": "FFmpeg/FFmpeg",
|
||||||
"version": "6efe500d2e"
|
"version": "c7b5f1537d"
|
||||||
},
|
},
|
||||||
"ffmpeg-ci": {
|
"ffmpeg-ci": {
|
||||||
"ci": true,
|
"ci": true,
|
||||||
@@ -333,6 +333,11 @@
|
|||||||
"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",
|
||||||
|
|||||||
Vendored
+628
-583
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+548
-503
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+550
-508
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+573
-527
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
+33
-21
@@ -1,32 +1,44 @@
|
|||||||
# 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 command line arguments.
|
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same 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.
|
||||||
- `-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.
|
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
||||||
- `-f`: Use fullscreen.
|
- `--config/-c`: Specify alternate configuration file.
|
||||||
- `-u <number>`: Select the index of the user to load as.
|
- `--fullscreen/-f`: Use fullscreen.
|
||||||
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
|
- `--help/-h`: Display help.
|
||||||
|
- `--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-cli
|
`eden` (provided with `--room`), and `eden-room` supports the following options as well:
|
||||||
|
|
||||||
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
- `-n/--room-name`: The name of the room.
|
||||||
- `--config/-c`: Specify alternate configuration file.
|
- `-d/--room-description`: The room description.
|
||||||
- `--fullscreen/-f`: Set fullscreen.
|
- `-s/--bind-address`: The bind address for the room.
|
||||||
- `--help/-h`: Display help.
|
- `-p/--port`: The port used for the room.
|
||||||
- `--game/-g`: Specify the game to run.
|
- `-m/--max-members`: The maximum number of players for this room.
|
||||||
- `--multiplayer/-m`: Specify multiplayer options.
|
- `-w/--password`: The password for the room.
|
||||||
- `--program/-p`: Specify the program arguments to pass (optional).
|
- `-g/--preferred-game`: The preferred game for this room.
|
||||||
- `--user/-u`: Specify the user index.
|
- `-i/--preferred-game-id`: The preferred game-id for this room.
|
||||||
- `--version/-v`: Display version and quit.
|
- `-u/--username`: The username used for announce.
|
||||||
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
|
- `-t/--token`: The token used for announce.
|
||||||
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
- `-a/--web-api-url`: yuzu Web API url.
|
||||||
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
- `-b/--ban-list-file`: The file for storing the room ban list.
|
||||||
- `--singlecore/-s`: Forces single-core regardless of settings.
|
- `-l/--log-file`: The file for storing the room log.
|
||||||
|
- `-h/--help`: Display this help and exit.
|
||||||
|
- `-v/--version`: Output version information and exit.
|
||||||
|
|||||||
Vendored
+5
@@ -48,6 +48,11 @@ 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)
|
||||||
|
|||||||
-1
@@ -30,7 +30,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
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"),
|
||||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
|
||||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||||
RENDERER_DEBUG("debug"),
|
RENDERER_DEBUG("debug"),
|
||||||
|
|||||||
-7
@@ -908,13 +908,6 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.enable_buffer_history_description
|
descriptionId = R.string.enable_buffer_history_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
|
||||||
titleId = R.string.enable_gpu_buffer_readback,
|
|
||||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||||
|
|||||||
-1
@@ -549,7 +549,6 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
|
||||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||||
|
|
||||||
add(HeaderSetting(R.string.hacks))
|
add(HeaderSetting(R.string.hacks))
|
||||||
|
|||||||
@@ -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) {
|
||||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const u64 program_id = FileSys::GetBaseTitleID(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")) {
|
||||||
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
|
||||||
if (update_id == program_id) {
|
if (update_id == program_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -580,8 +580,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||||
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||||
|
|
||||||
@@ -1099,7 +1097,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">فوري</string>
|
<string name="gpu_fence_behavior_immediate">فوري</string>
|
||||||
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
||||||
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
||||||
<string name="gpu_fence_behavior_strict">صارم</string>
|
|
||||||
|
|
||||||
<string name="vram_usage_conservative">محافظ</string>
|
<string name="vram_usage_conservative">محافظ</string>
|
||||||
<string name="vram_usage_aggressive">عدواني</string>
|
<string name="vram_usage_aggressive">عدواني</string>
|
||||||
|
|||||||
@@ -967,7 +967,6 @@ Wirklich fortfahren?</string>
|
|||||||
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Genau</string>
|
<string name="gpu_fence_behavior_accurate">Genau</string>
|
||||||
<string name="gpu_fence_behavior_strict">Strikt</string>
|
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Konservativ</string>
|
<string name="vram_usage_conservative">Konservativ</string>
|
||||||
<string name="vram_usage_aggressive">Aggressiv</string>
|
<string name="vram_usage_aggressive">Aggressiv</string>
|
||||||
|
|||||||
@@ -289,6 +289,7 @@
|
|||||||
<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>
|
||||||
@@ -523,8 +524,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||||
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
||||||
|
|
||||||
@@ -1037,7 +1036,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
||||||
<string name="gpu_fence_behavior_strict">Estricto</string>
|
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Conservador</string>
|
<string name="vram_usage_conservative">Conservador</string>
|
||||||
<string name="vram_usage_aggressive">Agresivo</string>
|
<string name="vram_usage_aggressive">Agresivo</string>
|
||||||
|
|||||||
@@ -318,6 +318,13 @@
|
|||||||
<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>
|
||||||
@@ -328,12 +335,14 @@
|
|||||||
<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>
|
||||||
@@ -560,8 +569,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
||||||
<string name="enable_gpu_buffer_readback">Включить обратное чтение буфера ГПУ</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку.</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||||
|
|
||||||
@@ -1079,7 +1086,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Точный</string>
|
<string name="gpu_fence_behavior_accurate">Точный</string>
|
||||||
<string name="gpu_fence_behavior_strict">Строгий</string>
|
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Консервативный</string>
|
<string name="vram_usage_conservative">Консервативный</string>
|
||||||
<string name="vram_usage_aggressive">Агрессивный</string>
|
<string name="vram_usage_aggressive">Агрессивный</string>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?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>
|
||||||
@@ -290,6 +291,25 @@
|
|||||||
<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>
|
||||||
@@ -304,6 +324,13 @@
|
|||||||
<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>
|
||||||
@@ -314,12 +341,14 @@
|
|||||||
<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>
|
||||||
@@ -541,8 +570,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||||
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||||
|
|
||||||
@@ -611,6 +638,11 @@
|
|||||||
<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>
|
||||||
@@ -894,6 +926,8 @@
|
|||||||
<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>
|
||||||
@@ -1053,7 +1087,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">即时</string>
|
<string name="gpu_fence_behavior_immediate">即时</string>
|
||||||
<string name="gpu_fence_behavior_balanced">均衡</string>
|
<string name="gpu_fence_behavior_balanced">均衡</string>
|
||||||
<string name="gpu_fence_behavior_accurate">精确</string>
|
<string name="gpu_fence_behavior_accurate">精确</string>
|
||||||
<string name="gpu_fence_behavior_strict">严格</string>
|
|
||||||
|
|
||||||
<string name="vram_usage_conservative">保守式</string>
|
<string name="vram_usage_conservative">保守式</string>
|
||||||
<string name="vram_usage_aggressive">主动式</string>
|
<string name="vram_usage_aggressive">主动式</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>
|
||||||
@@ -561,8 +561,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||||
<string name="enable_gpu_buffer_readback">啟用 GPU 緩衝區讀回</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
||||||
|
|
||||||
@@ -1020,9 +1018,40 @@
|
|||||||
<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>
|
||||||
@@ -1035,6 +1064,8 @@
|
|||||||
|
|
||||||
<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 -->
|
||||||
|
|||||||
@@ -558,7 +558,6 @@
|
|||||||
<item>@string/gpu_fence_behavior_immediate</item>
|
<item>@string/gpu_fence_behavior_immediate</item>
|
||||||
<item>@string/gpu_fence_behavior_balanced</item>
|
<item>@string/gpu_fence_behavior_balanced</item>
|
||||||
<item>@string/gpu_fence_behavior_accurate</item>
|
<item>@string/gpu_fence_behavior_accurate</item>
|
||||||
<item>@string/gpu_fence_behavior_strict</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="gpuFenceBehaviorValues">
|
<integer-array name="gpuFenceBehaviorValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
|
|||||||
@@ -586,8 +586,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||||
<string name="enable_buffer_history">Enable buffer history</string>
|
<string name="enable_buffer_history">Enable buffer history</string>
|
||||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||||
|
|
||||||
@@ -1138,7 +1136,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
||||||
<string name="gpu_fence_behavior_strict">Strict</string>
|
|
||||||
|
|
||||||
<!-- ASTC Decoding Method Choices -->
|
<!-- ASTC Decoding Method Choices -->
|
||||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ add_library(
|
|||||||
fs/fs_util.h
|
fs/fs_util.h
|
||||||
fs/path_util.cpp
|
fs/path_util.cpp
|
||||||
fs/path_util.h
|
fs/path_util.h
|
||||||
hash.h
|
|
||||||
heap_tracker.cpp
|
heap_tracker.cpp
|
||||||
heap_tracker.h
|
heap_tracker.h
|
||||||
hex_util.cpp
|
hex_util.cpp
|
||||||
@@ -76,7 +75,6 @@ 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
|
||||||
@@ -138,6 +136,8 @@ 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
|
||||||
@@ -148,6 +148,10 @@ 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;-Wno-missing-declarations;-Wno-shadow>")
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
+252
-251
@@ -12,93 +12,94 @@
|
|||||||
#include "common/android/multiplayer/multiplayer.h"
|
#include "common/android/multiplayer/multiplayer.h"
|
||||||
#include <network/network.h>
|
#include <network/network.h>
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
JavaVM *java_vm;
|
||||||
|
jclass native_library_class;
|
||||||
|
jclass disk_cache_progress_class;
|
||||||
|
jclass load_callback_stage_class;
|
||||||
|
jclass game_dir_class;
|
||||||
|
jmethodID game_dir_constructor;
|
||||||
|
jmethodID exit_emulation_activity;
|
||||||
|
jmethodID disk_cache_load_progress;
|
||||||
|
jmethodID on_emulation_started;
|
||||||
|
jmethodID on_emulation_stopped;
|
||||||
|
jmethodID on_program_changed;
|
||||||
|
jmethodID copy_to_storage;
|
||||||
|
jmethodID file_exists;
|
||||||
|
jmethodID file_extension;
|
||||||
|
|
||||||
static JavaVM *s_java_vm;
|
jclass game_class;
|
||||||
static jclass s_native_library_class;
|
jmethodID game_constructor;
|
||||||
static jclass s_disk_cache_progress_class;
|
jfieldID game_title_field;
|
||||||
static jclass s_load_callback_stage_class;
|
jfieldID game_path_field;
|
||||||
static jclass s_game_dir_class;
|
jfieldID game_program_id_field;
|
||||||
static jmethodID s_game_dir_constructor;
|
jfieldID game_developer_field;
|
||||||
static jmethodID s_exit_emulation_activity;
|
jfieldID game_version_field;
|
||||||
static jmethodID s_disk_cache_load_progress;
|
jfieldID game_is_homebrew_field;
|
||||||
static jmethodID s_on_emulation_started;
|
|
||||||
static jmethodID s_on_emulation_stopped;
|
|
||||||
static jmethodID s_on_program_changed;
|
|
||||||
static jmethodID s_copy_to_storage;
|
|
||||||
static jmethodID s_file_exists;
|
|
||||||
static jmethodID s_file_extension;
|
|
||||||
|
|
||||||
static jclass s_game_class;
|
jclass string_class;
|
||||||
static jmethodID s_game_constructor;
|
jclass pair_class;
|
||||||
static jfieldID s_game_title_field;
|
jmethodID pair_constructor;
|
||||||
static jfieldID s_game_path_field;
|
jfieldID pair_first_field;
|
||||||
static jfieldID s_game_program_id_field;
|
jfieldID pair_second_field;
|
||||||
static jfieldID s_game_developer_field;
|
|
||||||
static jfieldID s_game_version_field;
|
|
||||||
static jfieldID s_game_is_homebrew_field;
|
|
||||||
|
|
||||||
static jclass s_string_class;
|
jclass overlay_control_data_class;
|
||||||
static jclass s_pair_class;
|
jmethodID overlay_control_data_constructor;
|
||||||
static jmethodID s_pair_constructor;
|
jfieldID overlay_control_data_id_field;
|
||||||
static jfieldID s_pair_first_field;
|
jfieldID overlay_control_data_enabled_field;
|
||||||
static jfieldID s_pair_second_field;
|
jfieldID overlay_control_data_individual_scale_field;
|
||||||
|
jfieldID overlay_control_data_landscape_position_field;
|
||||||
|
jfieldID overlay_control_data_portrait_position_field;
|
||||||
|
jfieldID overlay_control_data_foldable_position_field;
|
||||||
|
|
||||||
static jclass s_overlay_control_data_class;
|
jclass patch_class;
|
||||||
static jmethodID s_overlay_control_data_constructor;
|
jmethodID patch_constructor;
|
||||||
static jfieldID s_overlay_control_data_id_field;
|
jfieldID patch_enabled_field;
|
||||||
static jfieldID s_overlay_control_data_enabled_field;
|
jfieldID patch_name_field;
|
||||||
static jfieldID s_overlay_control_data_individual_scale_field;
|
jfieldID patch_version_field;
|
||||||
static jfieldID s_overlay_control_data_landscape_position_field;
|
jfieldID patch_type_field;
|
||||||
static jfieldID s_overlay_control_data_portrait_position_field;
|
jfieldID patch_program_id_field;
|
||||||
static jfieldID s_overlay_control_data_foldable_position_field;
|
jfieldID patch_title_id_field;
|
||||||
|
|
||||||
static jclass s_patch_class;
|
jclass double_class;
|
||||||
static jmethodID s_patch_constructor;
|
jmethodID double_constructor;
|
||||||
static jfieldID s_patch_enabled_field;
|
jmethodID double_value_method;
|
||||||
static jfieldID s_patch_name_field;
|
|
||||||
static jfieldID s_patch_version_field;
|
|
||||||
static jfieldID s_patch_type_field;
|
|
||||||
static jfieldID s_patch_program_id_field;
|
|
||||||
static jfieldID s_patch_title_id_field;
|
|
||||||
|
|
||||||
static jclass s_double_class;
|
jclass integer_class;
|
||||||
static jmethodID s_double_constructor;
|
jmethodID integer_constructor;
|
||||||
static jmethodID s_double_value_method;
|
jmethodID integer_value_method;
|
||||||
|
|
||||||
static jclass s_integer_class;
|
jclass boolean_class;
|
||||||
static jmethodID s_integer_constructor;
|
jmethodID boolean_constructor;
|
||||||
static jmethodID s_integer_value_method;
|
jmethodID boolean_value_method;
|
||||||
|
|
||||||
static jclass s_boolean_class;
|
jclass player_input_class;
|
||||||
static jmethodID s_boolean_constructor;
|
jmethodID player_input_constructor;
|
||||||
static jmethodID s_boolean_value_method;
|
jfieldID player_input_connected_field;
|
||||||
|
jfieldID player_input_buttons_field;
|
||||||
|
jfieldID player_input_analogs_field;
|
||||||
|
jfieldID player_input_motions_field;
|
||||||
|
jfieldID player_input_vibration_enabled_field;
|
||||||
|
jfieldID player_input_vibration_strength_field;
|
||||||
|
jfieldID player_input_body_color_left_field;
|
||||||
|
jfieldID player_input_body_color_right_field;
|
||||||
|
jfieldID player_input_button_color_left_field;
|
||||||
|
jfieldID player_input_button_color_right_field;
|
||||||
|
jfieldID player_input_profile_name_field;
|
||||||
|
jfieldID player_input_use_system_vibrator_field;
|
||||||
|
|
||||||
static jclass s_player_input_class;
|
jclass yuzu_input_device_interface;
|
||||||
static jmethodID s_player_input_constructor;
|
jmethodID yuzu_input_device_get_name;
|
||||||
static jfieldID s_player_input_connected_field;
|
jmethodID yuzu_input_device_get_guid;
|
||||||
static jfieldID s_player_input_buttons_field;
|
jmethodID yuzu_input_device_get_port;
|
||||||
static jfieldID s_player_input_analogs_field;
|
jmethodID yuzu_input_device_get_supports_vibration;
|
||||||
static jfieldID s_player_input_motions_field;
|
jmethodID yuzu_input_device_vibrate;
|
||||||
static jfieldID s_player_input_vibration_enabled_field;
|
jmethodID yuzu_input_device_get_axes;
|
||||||
static jfieldID s_player_input_vibration_strength_field;
|
jmethodID yuzu_input_device_has_keys;
|
||||||
static jfieldID s_player_input_body_color_left_field;
|
|
||||||
static jfieldID s_player_input_body_color_right_field;
|
|
||||||
static jfieldID s_player_input_button_color_left_field;
|
|
||||||
static jfieldID s_player_input_button_color_right_field;
|
|
||||||
static jfieldID s_player_input_profile_name_field;
|
|
||||||
static jfieldID s_player_input_use_system_vibrator_field;
|
|
||||||
|
|
||||||
static jclass s_yuzu_input_device_interface;
|
jmethodID add_netplay_message;
|
||||||
static jmethodID s_yuzu_input_device_get_name;
|
jmethodID clear_chat;
|
||||||
static jmethodID s_yuzu_input_device_get_guid;
|
} state;
|
||||||
static jmethodID s_yuzu_input_device_get_port;
|
|
||||||
static jmethodID s_yuzu_input_device_get_supports_vibration;
|
|
||||||
static jmethodID s_yuzu_input_device_vibrate;
|
|
||||||
static jmethodID s_yuzu_input_device_get_axes;
|
|
||||||
static jmethodID s_yuzu_input_device_has_keys;
|
|
||||||
|
|
||||||
static jmethodID s_add_netplay_message;
|
|
||||||
static jmethodID s_clear_chat;
|
|
||||||
|
|
||||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||||
|
|
||||||
@@ -106,14 +107,14 @@ namespace Common::Android {
|
|||||||
JNIEnv *GetEnvForThread() {
|
JNIEnv *GetEnvForThread() {
|
||||||
thread_local static struct OwnedEnv {
|
thread_local static struct OwnedEnv {
|
||||||
OwnedEnv() {
|
OwnedEnv() {
|
||||||
status = s_java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
status = state.java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||||
if (status == JNI_EDETACHED)
|
if (status == JNI_EDETACHED)
|
||||||
s_java_vm->AttachCurrentThread(&env, nullptr);
|
state.java_vm->AttachCurrentThread(&env, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
~OwnedEnv() {
|
~OwnedEnv() {
|
||||||
if (status == JNI_EDETACHED)
|
if (status == JNI_EDETACHED)
|
||||||
s_java_vm->DetachCurrentThread();
|
state.java_vm->DetachCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
@@ -123,303 +124,303 @@ namespace Common::Android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jclass GetNativeLibraryClass() {
|
jclass GetNativeLibraryClass() {
|
||||||
return s_native_library_class;
|
return state.native_library_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDiskCacheProgressClass() {
|
jclass GetDiskCacheProgressClass() {
|
||||||
return s_disk_cache_progress_class;
|
return state.disk_cache_progress_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDiskCacheLoadCallbackStageClass() {
|
jclass GetDiskCacheLoadCallbackStageClass() {
|
||||||
return s_load_callback_stage_class;
|
return state.load_callback_stage_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetGameDirClass() {
|
jclass GetGameDirClass() {
|
||||||
return s_game_dir_class;
|
return state.game_dir_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetGameDirConstructor() {
|
jmethodID GetGameDirConstructor() {
|
||||||
return s_game_dir_constructor;
|
return state.game_dir_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetExitEmulationActivity() {
|
jmethodID GetExitEmulationActivity() {
|
||||||
return s_exit_emulation_activity;
|
return state.exit_emulation_activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDiskCacheLoadProgress() {
|
jmethodID GetDiskCacheLoadProgress() {
|
||||||
return s_disk_cache_load_progress;
|
return state.disk_cache_load_progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetCopyToStorage() {
|
jmethodID GetCopyToStorage() {
|
||||||
return s_copy_to_storage;
|
return state.copy_to_storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetFileExists() {
|
jmethodID GetFileExists() {
|
||||||
return s_file_exists;
|
return state.file_exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetFileExtension() {
|
jmethodID GetFileExtension() {
|
||||||
return s_file_extension;
|
return state.file_extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnEmulationStarted() {
|
jmethodID GetOnEmulationStarted() {
|
||||||
return s_on_emulation_started;
|
return state.on_emulation_started;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnEmulationStopped() {
|
jmethodID GetOnEmulationStopped() {
|
||||||
return s_on_emulation_stopped;
|
return state.on_emulation_stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnProgramChanged() {
|
jmethodID GetOnProgramChanged() {
|
||||||
return s_on_program_changed;
|
return state.on_program_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetGameClass() {
|
jclass GetGameClass() {
|
||||||
return s_game_class;
|
return state.game_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetGameConstructor() {
|
jmethodID GetGameConstructor() {
|
||||||
return s_game_constructor;
|
return state.game_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameTitleField() {
|
jfieldID GetGameTitleField() {
|
||||||
return s_game_title_field;
|
return state.game_title_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGamePathField() {
|
jfieldID GetGamePathField() {
|
||||||
return s_game_path_field;
|
return state.game_path_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameProgramIdField() {
|
jfieldID GetGameProgramIdField() {
|
||||||
return s_game_program_id_field;
|
return state.game_program_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameDeveloperField() {
|
jfieldID GetGameDeveloperField() {
|
||||||
return s_game_developer_field;
|
return state.game_developer_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameVersionField() {
|
jfieldID GetGameVersionField() {
|
||||||
return s_game_version_field;
|
return state.game_version_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameIsHomebrewField() {
|
jfieldID GetGameIsHomebrewField() {
|
||||||
return s_game_is_homebrew_field;
|
return state.game_is_homebrew_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetStringClass() {
|
jclass GetStringClass() {
|
||||||
return s_string_class;
|
return state.string_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPairClass() {
|
jclass GetPairClass() {
|
||||||
return s_pair_class;
|
return state.pair_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPairConstructor() {
|
jmethodID GetPairConstructor() {
|
||||||
return s_pair_constructor;
|
return state.pair_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPairFirstField() {
|
jfieldID GetPairFirstField() {
|
||||||
return s_pair_first_field;
|
return state.pair_first_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPairSecondField() {
|
jfieldID GetPairSecondField() {
|
||||||
return s_pair_second_field;
|
return state.pair_second_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetOverlayControlDataClass() {
|
jclass GetOverlayControlDataClass() {
|
||||||
return s_overlay_control_data_class;
|
return state.overlay_control_data_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOverlayControlDataConstructor() {
|
jmethodID GetOverlayControlDataConstructor() {
|
||||||
return s_overlay_control_data_constructor;
|
return state.overlay_control_data_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataIdField() {
|
jfieldID GetOverlayControlDataIdField() {
|
||||||
return s_overlay_control_data_id_field;
|
return state.overlay_control_data_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataEnabledField() {
|
jfieldID GetOverlayControlDataEnabledField() {
|
||||||
return s_overlay_control_data_enabled_field;
|
return state.overlay_control_data_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataIndividualScaleField() {
|
jfieldID GetOverlayControlDataIndividualScaleField() {
|
||||||
return s_overlay_control_data_individual_scale_field;
|
return state.overlay_control_data_individual_scale_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataLandscapePositionField() {
|
jfieldID GetOverlayControlDataLandscapePositionField() {
|
||||||
return s_overlay_control_data_landscape_position_field;
|
return state.overlay_control_data_landscape_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataPortraitPositionField() {
|
jfieldID GetOverlayControlDataPortraitPositionField() {
|
||||||
return s_overlay_control_data_portrait_position_field;
|
return state.overlay_control_data_portrait_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataFoldablePositionField() {
|
jfieldID GetOverlayControlDataFoldablePositionField() {
|
||||||
return s_overlay_control_data_foldable_position_field;
|
return state.overlay_control_data_foldable_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPatchClass() {
|
jclass GetPatchClass() {
|
||||||
return s_patch_class;
|
return state.patch_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPatchConstructor() {
|
jmethodID GetPatchConstructor() {
|
||||||
return s_patch_constructor;
|
return state.patch_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchEnabledField() {
|
jfieldID GetPatchEnabledField() {
|
||||||
return s_patch_enabled_field;
|
return state.patch_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchNameField() {
|
jfieldID GetPatchNameField() {
|
||||||
return s_patch_name_field;
|
return state.patch_name_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchVersionField() {
|
jfieldID GetPatchVersionField() {
|
||||||
return s_patch_version_field;
|
return state.patch_version_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchTypeField() {
|
jfieldID GetPatchTypeField() {
|
||||||
return s_patch_type_field;
|
return state.patch_type_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchProgramIdField() {
|
jfieldID GetPatchProgramIdField() {
|
||||||
return s_patch_program_id_field;
|
return state.patch_program_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchTitleIdField() {
|
jfieldID GetPatchTitleIdField() {
|
||||||
return s_patch_title_id_field;
|
return state.patch_title_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDoubleClass() {
|
jclass GetDoubleClass() {
|
||||||
return s_double_class;
|
return state.double_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDoubleConstructor() {
|
jmethodID GetDoubleConstructor() {
|
||||||
return s_double_constructor;
|
return state.double_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDoubleValueMethod() {
|
jmethodID GetDoubleValueMethod() {
|
||||||
return s_double_value_method;
|
return state.double_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetIntegerClass() {
|
jclass GetIntegerClass() {
|
||||||
return s_integer_class;
|
return state.integer_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetIntegerConstructor() {
|
jmethodID GetIntegerConstructor() {
|
||||||
return s_integer_constructor;
|
return state.integer_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetIntegerValueMethod() {
|
jmethodID GetIntegerValueMethod() {
|
||||||
return s_integer_value_method;
|
return state.integer_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetBooleanClass() {
|
jclass GetBooleanClass() {
|
||||||
return s_boolean_class;
|
return state.boolean_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetBooleanConstructor() {
|
jmethodID GetBooleanConstructor() {
|
||||||
return s_boolean_constructor;
|
return state.boolean_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetBooleanValueMethod() {
|
jmethodID GetBooleanValueMethod() {
|
||||||
return s_boolean_value_method;
|
return state.boolean_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPlayerInputClass() {
|
jclass GetPlayerInputClass() {
|
||||||
return s_player_input_class;
|
return state.player_input_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPlayerInputConstructor() {
|
jmethodID GetPlayerInputConstructor() {
|
||||||
return s_player_input_constructor;
|
return state.player_input_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputConnectedField() {
|
jfieldID GetPlayerInputConnectedField() {
|
||||||
return s_player_input_connected_field;
|
return state.player_input_connected_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonsField() {
|
jfieldID GetPlayerInputButtonsField() {
|
||||||
return s_player_input_buttons_field;
|
return state.player_input_buttons_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputAnalogsField() {
|
jfieldID GetPlayerInputAnalogsField() {
|
||||||
return s_player_input_analogs_field;
|
return state.player_input_analogs_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputMotionsField() {
|
jfieldID GetPlayerInputMotionsField() {
|
||||||
return s_player_input_motions_field;
|
return state.player_input_motions_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputVibrationEnabledField() {
|
jfieldID GetPlayerInputVibrationEnabledField() {
|
||||||
return s_player_input_vibration_enabled_field;
|
return state.player_input_vibration_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputVibrationStrengthField() {
|
jfieldID GetPlayerInputVibrationStrengthField() {
|
||||||
return s_player_input_vibration_strength_field;
|
return state.player_input_vibration_strength_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputBodyColorLeftField() {
|
jfieldID GetPlayerInputBodyColorLeftField() {
|
||||||
return s_player_input_body_color_left_field;
|
return state.player_input_body_color_left_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputBodyColorRightField() {
|
jfieldID GetPlayerInputBodyColorRightField() {
|
||||||
return s_player_input_body_color_right_field;
|
return state.player_input_body_color_right_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonColorLeftField() {
|
jfieldID GetPlayerInputButtonColorLeftField() {
|
||||||
return s_player_input_button_color_left_field;
|
return state.player_input_button_color_left_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonColorRightField() {
|
jfieldID GetPlayerInputButtonColorRightField() {
|
||||||
return s_player_input_button_color_right_field;
|
return state.player_input_button_color_right_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputProfileNameField() {
|
jfieldID GetPlayerInputProfileNameField() {
|
||||||
return s_player_input_profile_name_field;
|
return state.player_input_profile_name_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputUseSystemVibratorField() {
|
jfieldID GetPlayerInputUseSystemVibratorField() {
|
||||||
return s_player_input_use_system_vibrator_field;
|
return state.player_input_use_system_vibrator_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetYuzuInputDeviceInterface() {
|
jclass GetYuzuInputDeviceInterface() {
|
||||||
return s_yuzu_input_device_interface;
|
return state.yuzu_input_device_interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetName() {
|
jmethodID GetYuzuDeviceGetName() {
|
||||||
return s_yuzu_input_device_get_name;
|
return state.yuzu_input_device_get_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetGUID() {
|
jmethodID GetYuzuDeviceGetGUID() {
|
||||||
return s_yuzu_input_device_get_guid;
|
return state.yuzu_input_device_get_guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetPort() {
|
jmethodID GetYuzuDeviceGetPort() {
|
||||||
return s_yuzu_input_device_get_port;
|
return state.yuzu_input_device_get_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetSupportsVibration() {
|
jmethodID GetYuzuDeviceGetSupportsVibration() {
|
||||||
return s_yuzu_input_device_get_supports_vibration;
|
return state.yuzu_input_device_get_supports_vibration;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceVibrate() {
|
jmethodID GetYuzuDeviceVibrate() {
|
||||||
return s_yuzu_input_device_vibrate;
|
return state.yuzu_input_device_vibrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetAxes() {
|
jmethodID GetYuzuDeviceGetAxes() {
|
||||||
return s_yuzu_input_device_get_axes;
|
return state.yuzu_input_device_get_axes;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceHasKeys() {
|
jmethodID GetYuzuDeviceHasKeys() {
|
||||||
return s_yuzu_input_device_has_keys;
|
return state.yuzu_input_device_has_keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetAddNetPlayMessage() {
|
jmethodID GetAddNetPlayMessage() {
|
||||||
return s_add_netplay_message;
|
return state.add_netplay_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID ClearChat() {
|
jmethodID ClearChat() {
|
||||||
return s_clear_chat;
|
return state.clear_chat;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -436,20 +437,20 @@ namespace Common::Android {
|
|||||||
|
|
||||||
// UnInitialize Android Storage
|
// UnInitialize Android Storage
|
||||||
Common::FS::Android::UnRegisterCallbacks();
|
Common::FS::Android::UnRegisterCallbacks();
|
||||||
env->DeleteGlobalRef(s_native_library_class);
|
env->DeleteGlobalRef(state.native_library_class);
|
||||||
env->DeleteGlobalRef(s_disk_cache_progress_class);
|
env->DeleteGlobalRef(state.disk_cache_progress_class);
|
||||||
env->DeleteGlobalRef(s_load_callback_stage_class);
|
env->DeleteGlobalRef(state.load_callback_stage_class);
|
||||||
env->DeleteGlobalRef(s_game_dir_class);
|
env->DeleteGlobalRef(state.game_dir_class);
|
||||||
env->DeleteGlobalRef(s_game_class);
|
env->DeleteGlobalRef(state.game_class);
|
||||||
env->DeleteGlobalRef(s_string_class);
|
env->DeleteGlobalRef(state.string_class);
|
||||||
env->DeleteGlobalRef(s_pair_class);
|
env->DeleteGlobalRef(state.pair_class);
|
||||||
env->DeleteGlobalRef(s_overlay_control_data_class);
|
env->DeleteGlobalRef(state.overlay_control_data_class);
|
||||||
env->DeleteGlobalRef(s_patch_class);
|
env->DeleteGlobalRef(state.patch_class);
|
||||||
env->DeleteGlobalRef(s_double_class);
|
env->DeleteGlobalRef(state.double_class);
|
||||||
env->DeleteGlobalRef(s_integer_class);
|
env->DeleteGlobalRef(state.integer_class);
|
||||||
env->DeleteGlobalRef(s_boolean_class);
|
env->DeleteGlobalRef(state.boolean_class);
|
||||||
env->DeleteGlobalRef(s_player_input_class);
|
env->DeleteGlobalRef(state.player_input_class);
|
||||||
env->DeleteGlobalRef(s_yuzu_input_device_interface);
|
env->DeleteGlobalRef(state.yuzu_input_device_interface);
|
||||||
|
|
||||||
// UnInitialize applets
|
// UnInitialize applets
|
||||||
SoftwareKeyboard::CleanupJNI(env);
|
SoftwareKeyboard::CleanupJNI(env);
|
||||||
@@ -463,7 +464,7 @@ namespace Common::Android {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Initialize(JavaVM* vm, JNIEnv *env) {
|
void Initialize(JavaVM* vm, JNIEnv *env) {
|
||||||
s_java_vm = vm;
|
state.java_vm = vm;
|
||||||
InitFFmpegOnLoad(vm);
|
InitFFmpegOnLoad(vm);
|
||||||
|
|
||||||
if (env->ExceptionCheck()) {
|
if (env->ExceptionCheck()) {
|
||||||
@@ -472,169 +473,169 @@ void Initialize(JavaVM* vm, JNIEnv *env) {
|
|||||||
|
|
||||||
// Initialize Java classes
|
// Initialize Java classes
|
||||||
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
||||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
state.native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||||
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
state.disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||||
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
||||||
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
state.load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||||
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
||||||
|
|
||||||
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
||||||
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
state.game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
||||||
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
state.game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
||||||
"(Ljava/lang/String;Z)V");
|
"(Ljava/lang/String;Z)V");
|
||||||
env->DeleteLocalRef(game_dir_class);
|
env->DeleteLocalRef(game_dir_class);
|
||||||
|
|
||||||
// Initialize methods
|
// Initialize methods
|
||||||
s_exit_emulation_activity =
|
state.exit_emulation_activity =
|
||||||
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
env->GetStaticMethodID(state.native_library_class, "exitEmulationActivity", "(I)V");
|
||||||
s_disk_cache_load_progress =
|
state.disk_cache_load_progress =
|
||||||
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
|
env->GetStaticMethodID(state.disk_cache_progress_class, "loadProgress", "(III)V");
|
||||||
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
|
state.copy_to_storage = env->GetStaticMethodID(state.native_library_class, "copyFileToStorage",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||||
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
|
state.file_exists = env->GetStaticMethodID(state.native_library_class, "exists",
|
||||||
"(Ljava/lang/String;)Z");
|
"(Ljava/lang/String;)Z");
|
||||||
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
|
state.file_extension = env->GetStaticMethodID(state.native_library_class, "getFileExtension",
|
||||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||||
s_on_emulation_started =
|
state.on_emulation_started =
|
||||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
|
env->GetStaticMethodID(state.native_library_class, "onEmulationStarted", "()V");
|
||||||
s_on_emulation_stopped =
|
state.on_emulation_stopped =
|
||||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
|
env->GetStaticMethodID(state.native_library_class, "onEmulationStopped", "(I)V");
|
||||||
s_on_program_changed =
|
state.on_program_changed =
|
||||||
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
|
env->GetStaticMethodID(state.native_library_class, "onProgramChanged", "(I)V");
|
||||||
|
|
||||||
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
||||||
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
state.game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
||||||
s_game_constructor = env->GetMethodID(game_class, "<init>",
|
state.game_constructor = env->GetMethodID(game_class, "<init>",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
||||||
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
||||||
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
state.game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
||||||
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
state.game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
||||||
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
state.game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
||||||
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
state.game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
||||||
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
state.game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
||||||
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
state.game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
||||||
env->DeleteLocalRef(game_class);
|
env->DeleteLocalRef(game_class);
|
||||||
|
|
||||||
const jclass string_class = env->FindClass("java/lang/String");
|
const jclass string_class = env->FindClass("java/lang/String");
|
||||||
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
state.string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
||||||
env->DeleteLocalRef(string_class);
|
env->DeleteLocalRef(string_class);
|
||||||
|
|
||||||
const jclass pair_class = env->FindClass("kotlin/Pair");
|
const jclass pair_class = env->FindClass("kotlin/Pair");
|
||||||
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
state.pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
||||||
s_pair_constructor =
|
state.pair_constructor =
|
||||||
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
||||||
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
state.pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
||||||
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
state.pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
||||||
env->DeleteLocalRef(pair_class);
|
env->DeleteLocalRef(pair_class);
|
||||||
|
|
||||||
const jclass overlay_control_data_class =
|
const jclass overlay_control_data_class =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
||||||
s_overlay_control_data_class =
|
state.overlay_control_data_class =
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
||||||
s_overlay_control_data_constructor =
|
state.overlay_control_data_constructor =
|
||||||
env->GetMethodID(overlay_control_data_class, "<init>",
|
env->GetMethodID(overlay_control_data_class, "<init>",
|
||||||
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
||||||
s_overlay_control_data_id_field =
|
state.overlay_control_data_id_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
||||||
s_overlay_control_data_enabled_field =
|
state.overlay_control_data_enabled_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
||||||
s_overlay_control_data_landscape_position_field =
|
state.overlay_control_data_landscape_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
||||||
s_overlay_control_data_portrait_position_field =
|
state.overlay_control_data_portrait_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
||||||
s_overlay_control_data_foldable_position_field =
|
state.overlay_control_data_foldable_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||||
s_overlay_control_data_individual_scale_field =
|
state.overlay_control_data_individual_scale_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
||||||
env->DeleteLocalRef(overlay_control_data_class);
|
env->DeleteLocalRef(overlay_control_data_class);
|
||||||
|
|
||||||
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
||||||
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
state.patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||||
s_patch_constructor = env->GetMethodID(
|
state.patch_constructor = env->GetMethodID(
|
||||||
patch_class, "<init>",
|
patch_class, "<init>",
|
||||||
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
||||||
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
state.patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||||
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
state.patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||||
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
state.patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||||
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
state.patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||||
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
state.patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||||
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
state.patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||||
env->DeleteLocalRef(patch_class);
|
env->DeleteLocalRef(patch_class);
|
||||||
|
|
||||||
const jclass double_class = env->FindClass("java/lang/Double");
|
const jclass double_class = env->FindClass("java/lang/Double");
|
||||||
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
state.double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||||
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
state.double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||||
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
state.double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
||||||
env->DeleteLocalRef(double_class);
|
env->DeleteLocalRef(double_class);
|
||||||
|
|
||||||
const jclass int_class = env->FindClass("java/lang/Integer");
|
const jclass int_class = env->FindClass("java/lang/Integer");
|
||||||
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
state.integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||||
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
state.integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||||
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
state.integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
||||||
env->DeleteLocalRef(int_class);
|
env->DeleteLocalRef(int_class);
|
||||||
|
|
||||||
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
||||||
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
state.boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||||
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
state.boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||||
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
state.boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
||||||
env->DeleteLocalRef(boolean_class);
|
env->DeleteLocalRef(boolean_class);
|
||||||
|
|
||||||
const jclass player_input_class =
|
const jclass player_input_class =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
||||||
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
state.player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
||||||
s_player_input_constructor = env->GetMethodID(
|
state.player_input_constructor = env->GetMethodID(
|
||||||
player_input_class, "<init>",
|
player_input_class, "<init>",
|
||||||
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
||||||
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
state.player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
||||||
s_player_input_buttons_field =
|
state.player_input_buttons_field =
|
||||||
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
||||||
s_player_input_analogs_field =
|
state.player_input_analogs_field =
|
||||||
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
||||||
s_player_input_motions_field =
|
state.player_input_motions_field =
|
||||||
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
||||||
s_player_input_vibration_enabled_field =
|
state.player_input_vibration_enabled_field =
|
||||||
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
||||||
s_player_input_vibration_strength_field =
|
state.player_input_vibration_strength_field =
|
||||||
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
||||||
s_player_input_body_color_left_field =
|
state.player_input_body_color_left_field =
|
||||||
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
||||||
s_player_input_body_color_right_field =
|
state.player_input_body_color_right_field =
|
||||||
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
||||||
s_player_input_button_color_left_field =
|
state.player_input_button_color_left_field =
|
||||||
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
||||||
s_player_input_button_color_right_field =
|
state.player_input_button_color_right_field =
|
||||||
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
||||||
s_player_input_profile_name_field =
|
state.player_input_profile_name_field =
|
||||||
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
||||||
s_player_input_use_system_vibrator_field =
|
state.player_input_use_system_vibrator_field =
|
||||||
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
||||||
env->DeleteLocalRef(player_input_class);
|
env->DeleteLocalRef(player_input_class);
|
||||||
|
|
||||||
const jclass yuzu_input_device_interface =
|
const jclass yuzu_input_device_interface =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
||||||
s_yuzu_input_device_interface =
|
state.yuzu_input_device_interface =
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
||||||
s_yuzu_input_device_get_name =
|
state.yuzu_input_device_get_name =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
||||||
s_yuzu_input_device_get_guid =
|
state.yuzu_input_device_get_guid =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
||||||
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
state.yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
||||||
"()I");
|
"()I");
|
||||||
s_yuzu_input_device_get_supports_vibration =
|
state.yuzu_input_device_get_supports_vibration =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
||||||
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
state.yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
||||||
"(F)V");
|
"(F)V");
|
||||||
s_yuzu_input_device_get_axes =
|
state.yuzu_input_device_get_axes =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
||||||
s_yuzu_input_device_has_keys =
|
state.yuzu_input_device_has_keys =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
||||||
env->DeleteLocalRef(yuzu_input_device_interface);
|
env->DeleteLocalRef(yuzu_input_device_interface);
|
||||||
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
|
state.add_netplay_message = env->GetStaticMethodID(state.native_library_class, "addNetPlayMessage",
|
||||||
"(ILjava/lang/String;)V");
|
"(ILjava/lang/String;)V");
|
||||||
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
|
state.clear_chat = env->GetStaticMethodID(state.native_library_class, "clearChat", "()V");
|
||||||
|
|
||||||
// Initialize Android Storage
|
// Initialize Android Storage
|
||||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
Common::FS::Android::RegisterCallbacks(env, state.native_library_class);
|
||||||
|
|
||||||
// Initialize applets
|
// Initialize applets
|
||||||
Common::Android::SoftwareKeyboard::InitJNI(env);
|
Common::Android::SoftwareKeyboard::InitJNI(env);
|
||||||
|
|||||||
+4
-18
@@ -22,36 +22,22 @@ template <typename T>
|
|||||||
return std::size_t(sizeof(T) * CHAR_BIT);
|
return std::size_t(sizeof(T) * CHAR_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
requires std::is_integral_v<T>
|
|
||||||
[[nodiscard]] constexpr u32 MostSignificantBit(const T value) {
|
|
||||||
return u32(sizeof(T) * CHAR_BIT - 1 - std::countl_zero(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires std::is_integral_v<T>
|
requires std::is_integral_v<T>
|
||||||
[[nodiscard]] constexpr T Log2Floor(const T value) {
|
[[nodiscard]] constexpr T Log2Floor(const T value) {
|
||||||
return T(MostSignificantBit<T>(value));
|
return std::bit_width(value) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires std::is_integral_v<T>
|
requires std::is_integral_v<T>
|
||||||
[[nodiscard]] constexpr T Log2Ceil(const T value) {
|
[[nodiscard]] constexpr T Log2Ceil(const T value) {
|
||||||
const T log2_f = Log2Floor<T>(value);
|
return std::bit_width(value - 1);
|
||||||
return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires std::is_integral_v<T>
|
|
||||||
[[nodiscard]] T NextPow2(T value) {
|
|
||||||
return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t bit_index, typename T>
|
template <size_t bit_index, typename T>
|
||||||
requires std::is_integral_v<T>
|
requires (std::is_integral_v<T> && bit_index < BitSize<T>())
|
||||||
[[nodiscard]] constexpr bool Bit(const T value) {
|
[[nodiscard]] constexpr bool Bit(const T value) {
|
||||||
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
|
return (T(value >> bit_index) & T(1)) == T(1);
|
||||||
return ((value >> bit_index) & T(1)) == T(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -25,26 +25,18 @@ template <typename T>
|
|||||||
requires std::is_unsigned_v<T>
|
requires std::is_unsigned_v<T>
|
||||||
inline std::size_t HashValue(T val) {
|
inline std::size_t HashValue(T val) {
|
||||||
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
||||||
const unsigned int length =
|
const unsigned int length = (std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
||||||
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
|
||||||
|
|
||||||
std::size_t seed = 0;
|
std::size_t seed = 0;
|
||||||
|
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
|
||||||
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
|
seed ^= std::size_t(val >> i) + (seed << 6) + (seed >> 2);
|
||||||
seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
|
return seed ^= std::size_t(val) + (seed << 6) + (seed >> 2);
|
||||||
}
|
|
||||||
|
|
||||||
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
|
|
||||||
|
|
||||||
return seed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t Bits>
|
template <size_t Bits>
|
||||||
struct HashCombineImpl {
|
struct HashCombineImpl {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T fn(T seed, T value) {
|
static inline T fn(T seed, T value) {
|
||||||
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
return seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
return seed;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -244,7 +244,8 @@ 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_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
|
, rdtsc_ns_integer{invariant_ ? 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_}
|
||||||
@@ -291,7 +292,7 @@ bool WallClock::IsNative() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
||||||
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
|
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
|
||||||
}
|
}
|
||||||
#elif defined(HAS_NCE)
|
#elif defined(HAS_NCE)
|
||||||
namespace {
|
namespace {
|
||||||
@@ -416,7 +417,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
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ 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;
|
||||||
|
|||||||
@@ -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,14 +15,14 @@ namespace Common {
|
|||||||
template <typename N, typename D>
|
template <typename N, typename D>
|
||||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||||
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
||||||
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
return N((D(number) + divisor - 1) / divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ceiled integer division with logarithmic divisor in base 2
|
/// Ceiled integer division with logarithmic divisor in base 2
|
||||||
template <typename N, typename D>
|
template <typename N, typename D>
|
||||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||||
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
||||||
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
return N((D(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
+16
-18
@@ -17,35 +17,33 @@
|
|||||||
|
|
||||||
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),
|
||||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
LPSTR(&err_str), 1, nullptr);
|
||||||
if (!res) {
|
if (res) {
|
||||||
return "(FormatMessageA failed to format error)";
|
|
||||||
}
|
|
||||||
std::string ret(err_str);
|
std::string ret(err_str);
|
||||||
LocalFree(err_str);
|
LocalFree(err_str);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
return "(FormatMessageA failed to format error)";
|
||||||
#else
|
#else
|
||||||
char err_str[255];
|
char err_str[255];
|
||||||
#if defined(__ANDROID__) || \
|
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
|
||||||
(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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <utility>
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
struct PairHash {
|
|
||||||
template <class T1, class T2>
|
|
||||||
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
|
|
||||||
std::size_t seed = std::hash<T1>()(pair.first);
|
|
||||||
boost::hash_combine(seed, std::hash<T2>()(pair.second));
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct IdentityHash {
|
|
||||||
[[nodiscard]] size_t operator()(T value) const noexcept {
|
|
||||||
return static_cast<size_t>(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
+10
-16
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
@@ -16,14 +19,12 @@ namespace Common {
|
|||||||
|
|
||||||
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
||||||
if (c >= 65 && c <= 70) {
|
if (c >= 65 && c <= 70) {
|
||||||
return static_cast<u8>(c - 55);
|
return u8(c - 55);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c >= 97 && c <= 102) {
|
if (c >= 97 && c <= 102) {
|
||||||
return static_cast<u8>(c - 87);
|
return u8(c - 87);
|
||||||
}
|
}
|
||||||
|
return u8(c - 48);
|
||||||
return static_cast<u8>(c - 48);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
||||||
@@ -31,35 +32,28 @@ namespace Common {
|
|||||||
template <std::size_t Size, bool le = false>
|
template <std::size_t Size, bool le = false>
|
||||||
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||||
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
||||||
|
|
||||||
std::array<u8, Size> out{};
|
std::array<u8, Size> out{};
|
||||||
if constexpr (le) {
|
if constexpr (le) {
|
||||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
||||||
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
||||||
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContiguousContainer>
|
template <typename ContiguousContainer>
|
||||||
|
requires std::is_same_v<typename ContiguousContainer::value_type, u8>
|
||||||
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
||||||
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
|
|
||||||
"Underlying type within the contiguous container must be u8.");
|
|
||||||
|
|
||||||
constexpr std::size_t pad_width = 2;
|
constexpr std::size_t pad_width = 2;
|
||||||
|
|
||||||
std::string out;
|
std::string out;
|
||||||
out.reserve(std::size(data) * pad_width);
|
out.reserve(std::size(data) * pad_width);
|
||||||
|
|
||||||
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
|
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
|
||||||
for (const u8 c : data) {
|
for (const u8 c : data)
|
||||||
out += fmt::format(format_str, c);
|
out += fmt::format(format_str, c);
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -10,6 +10,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
@@ -95,9 +96,10 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
Level GetLevelByName(const It begin, const It end) {
|
Level GetLevelByName(const It begin, const It end) {
|
||||||
|
std::string_view const sv{begin, end};
|
||||||
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
||||||
const char* level_name = GetLevelName(Level(i));
|
auto const level_name = GetLevelName(Level(i));
|
||||||
if (Common::ComparePartialString(begin, end, level_name))
|
if (boost::iequals(sv, level_name))
|
||||||
return Level(i);
|
return Level(i);
|
||||||
}
|
}
|
||||||
return Level::Count;
|
return Level::Count;
|
||||||
@@ -105,9 +107,10 @@ Level GetLevelByName(const It begin, const It end) {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
Class GetClassByName(const It begin, const It end) {
|
Class GetClassByName(const It begin, const It end) {
|
||||||
|
std::string_view const sv{begin, end};
|
||||||
for (u32 i = 0; i < u32(Class::Count); ++i) {
|
for (u32 i = 0; i < u32(Class::Count); ++i) {
|
||||||
const char* level_name = GetLogClassName(Class(i));
|
auto const level_name = GetLogClassName(Class(i));
|
||||||
if (Common::ComparePartialString(begin, end, level_name))
|
if (boost::iequals(sv, level_name))
|
||||||
return Class(i);
|
return Class(i);
|
||||||
}
|
}
|
||||||
return Class::Count;
|
return Class::Count;
|
||||||
@@ -120,12 +123,13 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||||||
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
|
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const Level level = GetLevelByName(level_separator + 1, end);
|
auto const sv = std::string_view{begin, level_separator};
|
||||||
|
auto const level = GetLevelByName(level_separator + 1, end);
|
||||||
if (level == Level::Count) {
|
if (level == Level::Count) {
|
||||||
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
|
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Common::ComparePartialString(begin, level_separator, "*")) {
|
if (boost::iequals(sv, "*")) {
|
||||||
instance.class_levels.fill(level);
|
instance.class_levels.fill(level);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// 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
|
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
@@ -15,17 +18,16 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
constexpr char KEY_VALUE_SEPARATOR = ':';
|
constexpr auto KEY_VALUE_SEPARATOR = ":";
|
||||||
constexpr char PARAM_SEPARATOR = ',';
|
constexpr auto PARAM_SEPARATOR = ",";
|
||||||
|
constexpr auto ESCAPE_CHARACTER = "$";
|
||||||
constexpr char ESCAPE_CHARACTER = '$';
|
constexpr auto KEY_VALUE_SEPARATOR_ESCAPE = "$0";
|
||||||
constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
|
constexpr auto PARAM_SEPARATOR_ESCAPE = "$1";
|
||||||
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
|
constexpr auto ESCAPE_CHARACTER_ESCAPE = "$2";
|
||||||
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
|
|
||||||
|
|
||||||
/// A placeholder for empty param packages to avoid empty strings
|
/// A placeholder for empty param packages to avoid empty strings
|
||||||
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
||||||
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
|
constexpr auto EMPTY_PLACEHOLDER = "[empty]";
|
||||||
|
|
||||||
ParamPackage::ParamPackage(const std::string& serialized) {
|
ParamPackage::ParamPackage(const std::string& serialized) {
|
||||||
if (serialized == EMPTY_PLACEHOLDER) {
|
if (serialized == EMPTY_PLACEHOLDER) {
|
||||||
@@ -33,20 +35,20 @@ ParamPackage::ParamPackage(const std::string& serialized) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> pairs;
|
std::vector<std::string> pairs;
|
||||||
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
|
boost::split(pairs, serialized, boost::is_any_of(PARAM_SEPARATOR));
|
||||||
|
|
||||||
for (const std::string& pair : pairs) {
|
for (const std::string& pair : pairs) {
|
||||||
std::vector<std::string> key_value;
|
std::vector<std::string> key_value;
|
||||||
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
|
boost::split(key_value, pair, boost::is_any_of(KEY_VALUE_SEPARATOR));
|
||||||
if (key_value.size() != 2) {
|
if (key_value.size() != 2) {
|
||||||
LOG_ERROR(Common, "invalid key pair {}", pair);
|
LOG_ERROR(Common, "invalid key pair {}", pair);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::string& part : key_value) {
|
for (auto& part : key_value) {
|
||||||
part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
|
boost::replace_all(part, KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR);
|
||||||
part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
|
boost::replace_all(part, PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR);
|
||||||
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
|
boost::replace_all(part, ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set(key_value[0], std::move(key_value[1]));
|
Set(key_value[0], std::move(key_value[1]));
|
||||||
@@ -63,10 +65,10 @@ std::string ParamPackage::Serialize() const {
|
|||||||
|
|
||||||
for (const auto& pair : data) {
|
for (const auto& pair : data) {
|
||||||
std::array<std::string, 2> key_value{{pair.first, pair.second}};
|
std::array<std::string, 2> key_value{{pair.first, pair.second}};
|
||||||
for (std::string& part : key_value) {
|
for (auto& part : key_value) {
|
||||||
part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
|
boost::replace_all(part, ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE);
|
||||||
part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
|
boost::replace_all(part, PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE);
|
||||||
part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
|
boost::replace_all(part, KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE);
|
||||||
}
|
}
|
||||||
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
|
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
#include "common/make_unique_for_overwrite.h"
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
@@ -38,8 +37,9 @@ public:
|
|||||||
ScratchBuffer() = default;
|
ScratchBuffer() = default;
|
||||||
|
|
||||||
explicit ScratchBuffer(size_type initial_capacity)
|
explicit ScratchBuffer(size_type initial_capacity)
|
||||||
: last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
|
: last_requested_size{initial_capacity}
|
||||||
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
, buffer_capacity{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 = Common::make_unique_for_overwrite<T[]>(size);
|
auto new_buffer = std::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 = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
|
buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity);
|
||||||
}
|
}
|
||||||
last_requested_size = size;
|
last_requested_size = size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,10 +178,6 @@ bool IsGPUFenceBehaviorAccurate() {
|
|||||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsGPUFenceBehaviorStrict() {
|
|
||||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsFastmemEnabled() {
|
bool IsFastmemEnabled() {
|
||||||
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
||||||
return bool(values.cpuopt_fastmem);
|
return bool(values.cpuopt_fastmem);
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ struct Values {
|
|||||||
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Strict,
|
GpuFenceBehavior::Accurate,
|
||||||
"gpu_fence_behavior",
|
"gpu_fence_behavior",
|
||||||
Category::RendererAdvanced,
|
Category::RendererAdvanced,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
@@ -655,13 +655,6 @@ struct Values {
|
|||||||
|
|
||||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
|
||||||
false,
|
|
||||||
"enable_gpu_buffer_readback",
|
|
||||||
Category::RendererAdvanced,
|
|
||||||
Specialization::Default,
|
|
||||||
true,
|
|
||||||
true};
|
|
||||||
|
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
@@ -979,7 +972,6 @@ bool IsDMALevelSafe();
|
|||||||
bool IsGPUFenceBehaviorDefault();
|
bool IsGPUFenceBehaviorDefault();
|
||||||
bool IsGPUFenceBehaviorBalanced();
|
bool IsGPUFenceBehaviorBalanced();
|
||||||
bool IsGPUFenceBehaviorAccurate();
|
bool IsGPUFenceBehaviorAccurate();
|
||||||
bool IsGPUFenceBehaviorStrict();
|
|
||||||
|
|
||||||
bool IsFastmemEnabled();
|
bool IsFastmemEnabled();
|
||||||
void SetNceEnabled(bool is_64bit);
|
void SetNceEnabled(bool is_64bit);
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ ENUM(VramUsageMode, Conservative, Aggressive);
|
|||||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||||
ENUM(GpuAccuracy, Low, High);
|
ENUM(GpuAccuracy, Low, High);
|
||||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
|
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate);
|
||||||
ENUM(CpuBackend, Dynarmic, Nce);
|
ENUM(CpuBackend, Dynarmic, Nce);
|
||||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
||||||
ENUM(CpuClock, Normal, Boost, Overclock)
|
ENUM(CpuClock, Normal, Boost, Overclock)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#include <algorithm>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#else
|
#else
|
||||||
@@ -20,19 +21,22 @@ namespace Common {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||||
|
static std::mutex vector_regions_mutex {};
|
||||||
|
|
||||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionInformation[1]);
|
||||||
|
|
||||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
if (code != EXCEPTION_ACCESS_VIOLATION || info->ExceptionRecord->ExceptionInformation[0] == 1) {
|
||||||
// Not our problem
|
// Not our problem
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 addr = 0, addr2 = 0;
|
u64 addr = 0, addr2 = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(vector_regions_mutex);
|
||||||
for (auto region: vector_regions) {
|
for (auto region: vector_regions) {
|
||||||
auto addr_shifted = exception_addr >> HostPageBits;
|
auto addr_shifted = exception_addr >> HostPageBits;
|
||||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||||
@@ -48,6 +52,7 @@ static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (addr == 0 && addr2 == 0) {
|
if (addr == 0 && addr2 == 0) {
|
||||||
// Not our problem
|
// Not our problem
|
||||||
@@ -77,8 +82,16 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
|||||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||||
|
} else if (info.State == MEM_COMMIT) {
|
||||||
|
DWORD old_protect {};
|
||||||
|
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||||
|
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
||||||
|
LOG_ERROR(HW_Memory, "Failed to change permissions of large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} else if (info.State != MEM_RESERVE) {
|
} else if (info.State != MEM_RESERVE) {
|
||||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped (state {:#x})", addr, info.State);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +109,21 @@ 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) {
|
||||||
@@ -108,7 +136,8 @@ void* AllocateMemoryPages(std::size_t size) noexcept {
|
|||||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
|
||||||
if (base != nullptr) {
|
if (base != nullptr) {
|
||||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
std::lock_guard lock(vector_regions_mutex);
|
||||||
|
vector_regions.emplace_back(reinterpret_cast<u64>(base) >> HostPageBits, (reinterpret_cast<u64>(base) + size) >> HostPageBits);
|
||||||
|
|
||||||
static std::once_flag flag;
|
static std::once_flag flag;
|
||||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||||
@@ -134,6 +163,8 @@ void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
|||||||
if (!base)
|
if (!base)
|
||||||
return;
|
return;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
std::lock_guard lock(vector_regions_mutex);
|
||||||
|
std::erase_if(vector_regions, [base](const auto& r) {return r.first == reinterpret_cast<u64>(base); });
|
||||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||||
#else
|
#else
|
||||||
ASSERT(munmap(base, size) == 0);
|
ASSERT(munmap(base, size) == 0);
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ constexpr u64 HostPageBits = 12;
|
|||||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||||
#else
|
#else
|
||||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
inline const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
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>
|
||||||
@@ -80,8 +81,8 @@ public:
|
|||||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsCommittedPage(index)) {
|
if (!IsCommittedPage(index) && !CommitPage(index)) {
|
||||||
CommitPage(index);
|
UNREACHABLE_MSG("Cannot access SparseLargeVector index {} with RW permission", index);
|
||||||
}
|
}
|
||||||
return base_ptr[index];
|
return base_ptr[index];
|
||||||
}
|
}
|
||||||
@@ -102,8 +103,7 @@ public:
|
|||||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!IsCommittedPage(index))
|
if (IsCommittedPage(index) || CommitPage(index))
|
||||||
CommitPage(index);
|
|
||||||
base_ptr[index] = value;
|
base_ptr[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,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 / sizeof(T))) {
|
if (IsCommittedPage(start)) {
|
||||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +124,16 @@ public:
|
|||||||
base = end_page;
|
base = end_page;
|
||||||
|
|
||||||
for (u64 page = base; page < end; page += HostPageSize) {
|
for (u64 page = base; page < end; page += HostPageSize) {
|
||||||
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
|
||||||
|
if (!IsCommittedPage(index)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
if (end - page >= HostPageSize) {
|
||||||
|
DecommitPage(index);
|
||||||
|
} else {
|
||||||
|
std::memset(reinterpret_cast<void*>(page), 0, end - page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,16 +176,30 @@ private:
|
|||||||
return (val >> (page & 63)) & 1;
|
return (val >> (page & 63)) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void CommitPage(std::size_t index) noexcept {
|
constexpr bool CommitPage(std::size_t index) noexcept {
|
||||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
CommitVectorPage(page, true);
|
if (!CommitVectorPage(page, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
if (mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
||||||
|
LOG_ERROR(Common_Memory, "Failed to commit large buffer region at index {}, error {}", index, strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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{};
|
||||||
|
|||||||
@@ -24,22 +24,6 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
|
||||||
std::string ToLower(const std::string_view sv) {
|
|
||||||
std::string str{sv};
|
|
||||||
std::transform(str.begin(), str.end(), str.begin(),
|
|
||||||
[](auto const c) { return char(std::tolower(c)); });
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a string uppercase
|
|
||||||
std::string ToUpper(const std::string_view sv) {
|
|
||||||
std::string str{sv};
|
|
||||||
std::transform(str.begin(), str.end(), str.begin(),
|
|
||||||
[](auto const c) { return char(std::toupper(c)); });
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
||||||
std::string* _pExtension) {
|
std::string* _pExtension) {
|
||||||
if (full_path.empty())
|
if (full_path.empty())
|
||||||
@@ -80,41 +64,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) {
|
|
||||||
std::istringstream iss(str);
|
|
||||||
output.resize(1);
|
|
||||||
|
|
||||||
while (std::getline(iss, *output.rbegin(), delim)) {
|
|
||||||
output.emplace_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
output.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string TabsToSpaces(int tab_size, std::string in) {
|
|
||||||
std::size_t i = 0;
|
|
||||||
|
|
||||||
while ((i = in.find('\t')) != std::string::npos) {
|
|
||||||
in.replace(i, 1, tab_size, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
|
|
||||||
std::size_t pos = 0;
|
|
||||||
|
|
||||||
if (src == dest)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
while ((pos = result.find(src, pos)) != std::string::npos) {
|
|
||||||
result.replace(pos, src.size(), dest);
|
|
||||||
pos += dest.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UTF16ToUTF8(std::u16string_view input) {
|
std::string UTF16ToUTF8(std::u16string_view input) {
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
return convert.to_bytes(input.data(), input.data() + input.size());
|
return convert.to_bytes(input.data(), input.data() + input.size());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
@@ -16,12 +16,6 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
|
||||||
[[nodiscard]] std::string ToLower(const std::string_view sv);
|
|
||||||
|
|
||||||
/// Make a string uppercase
|
|
||||||
[[nodiscard]] std::string ToUpper(const std::string_view sv);
|
|
||||||
|
|
||||||
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
}
|
}
|
||||||
@@ -29,37 +23,8 @@ namespace Common {
|
|||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns " hej " into "hej". Also handles tabs.
|
|
||||||
[[nodiscard]] inline std::string StripSpaces(const std::string_view str) noexcept {
|
|
||||||
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
|
||||||
if (str.npos != s)
|
|
||||||
return std::string{str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1)};
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "\"hello\"" is turned to "hello"
|
|
||||||
/// This one assumes that the string has already been space stripped in both
|
|
||||||
/// ends, as done by StripSpaces above, for example.
|
|
||||||
[[nodiscard]] inline std::string StripQuotes(const std::string_view s) noexcept {
|
|
||||||
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
|
|
||||||
return std::string{s.substr(1, s.size() - 2)};
|
|
||||||
return std::string{s};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] inline std::string StringFromBool(bool value) noexcept {
|
|
||||||
return value ? "True" : "False";
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
|
|
||||||
|
|
||||||
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
|
||||||
|
|
||||||
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
|
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
|
||||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
|
||||||
std::string* _pExtension);
|
|
||||||
|
|
||||||
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
|
|
||||||
const std::string& dest);
|
|
||||||
|
|
||||||
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
||||||
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
||||||
@@ -73,30 +38,13 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
|||||||
|
|
||||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
|
||||||
* `other` for equality.
|
|
||||||
*/
|
|
||||||
template <typename InIt>
|
|
||||||
[[nodiscard]] inline bool ComparePartialString(InIt begin, InIt end, const char* other) noexcept {
|
|
||||||
for (; begin != end && *other != '\0'; ++begin, ++other) {
|
|
||||||
if (*begin != *other) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Only return true if both strings finished at the same point
|
|
||||||
return (begin == end) == (*other == '\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
/// NUL-terminated then the string ends at max_len characters.
|
/// NUL-terminated then the string ends at max_len characters.
|
||||||
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
|
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len);
|
||||||
std::size_t max_len);
|
|
||||||
|
|
||||||
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||||
/// max_len_bytes.
|
/// max_len_bytes.
|
||||||
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len);
|
||||||
std::size_t max_len);
|
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#undef _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "zstd.h"
|
||||||
|
#define g_ZSTD_threading_useless_symbol g_ZSTD_zbic_threading_useless_symbol
|
||||||
|
#include "zstd.c"
|
||||||
|
#undef g_ZSTD_threading_useless_symbol
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// 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
|
||||||
@@ -23,6 +23,8 @@ 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
|
||||||
@@ -1203,6 +1205,9 @@ 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)
|
||||||
|
|||||||
@@ -22,21 +22,15 @@ 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()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
|
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
|
||||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||||
return m_memory.Read8(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||||
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
|
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||||
return m_memory.Read16(vaddr);
|
case sizeof(u8): return m_memory.Read8(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) {
|
||||||
@@ -50,27 +44,17 @@ 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::MemoryWrite8(u32 vaddr, u8 value) {
|
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||||
m_memory.Write8(vaddr, value);
|
switch (size) {
|
||||||
|
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);
|
||||||
|
|||||||
@@ -30,18 +30,12 @@ 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);
|
||||||
u8 MemoryRead8(u32 vaddr) override;
|
u64 MemoryRead(u32 vaddr, size_t size) 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 MemoryWrite8(u32 vaddr, u8 value) override;
|
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) 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;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@@ -21,21 +22,15 @@ 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()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
|
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
|
||||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||||
return m_memory.Read8(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||||
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
|
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||||
return m_memory.Read16(vaddr);
|
case sizeof(u8): return m_memory.Read8(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);
|
||||||
@@ -53,24 +48,15 @@ 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::MemoryWrite8(u64 vaddr, u8 value) {
|
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||||
m_memory.Write8(vaddr, value);
|
switch (size) {
|
||||||
|
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));
|
||||||
|
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
||||||
|
default: UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
|
|
||||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
|
||||||
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) {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
#include <dynarmic/interface/A64/a64.h>
|
#include <dynarmic/interface/A64/a64.h>
|
||||||
#include <dynarmic/interface/code_page.h>
|
#include <dynarmic/interface/code_page.h>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.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;
|
||||||
@@ -36,19 +36,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
|||||||
public:
|
public:
|
||||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
||||||
|
|
||||||
u8 MemoryRead8(u64 vaddr) override;
|
u64 MemoryRead(u64 vaddr, size_t size) 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 MemoryWrite8(u64 vaddr, u8 value) override;
|
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) 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;
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||||
/// Initialization
|
/// Initialization
|
||||||
system.RegisterCoreThread(core);
|
system.RegisterCoreThread(core);
|
||||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
||||||
@@ -178,26 +178,19 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
|||||||
auto& data = core_data[core];
|
auto& data = core_data[core];
|
||||||
data.host_context = Common::Fiber::ThreadToFiber();
|
data.host_context = Common::Fiber::ThreadToFiber();
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
SCOPE_EXIT {
|
|
||||||
data.host_context->Exit();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Running
|
// Running
|
||||||
if (!gpu_barrier->Sync(token)) {
|
gpu_barrier->arrive_and_wait();
|
||||||
return;
|
if (!stop_token.stop_requested()) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_async_gpu && !is_multicore) {
|
if (!is_async_gpu && !is_multicore) {
|
||||||
system.GPU().ObtainContext();
|
system.GPU().ObtainContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
auto& scheduler = *kernel.CurrentScheduler();
|
auto& scheduler = *kernel.CurrentScheduler();
|
||||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||||
Kernel::SetCurrentThread(kernel, thread);
|
Kernel::SetCurrentThread(kernel, thread);
|
||||||
|
|
||||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||||
|
}
|
||||||
|
data.host_context->Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <barrier>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -52,7 +53,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnGpuReady() {
|
void OnGpuReady() {
|
||||||
gpu_barrier->Sync();
|
gpu_barrier->arrive_and_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
@@ -95,7 +96,7 @@ private:
|
|||||||
|
|
||||||
static constexpr std::size_t max_cycle_runs = 5;
|
static constexpr std::size_t max_cycle_runs = 5;
|
||||||
|
|
||||||
std::optional<Common::Barrier> gpu_barrier{};
|
std::optional<std::barrier<>> gpu_barrier{};
|
||||||
struct CoreData {
|
struct CoreData {
|
||||||
std::shared_ptr<Common::Fiber> host_context;
|
std::shared_ptr<Common::Fiber> host_context;
|
||||||
std::jthread host_thread;
|
std::jthread host_thread;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
#include "common/fs/file.h"
|
#include "common/fs/file.h"
|
||||||
@@ -622,7 +623,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
|
|||||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||||
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
||||||
} else {
|
} else {
|
||||||
out[0] = Common::ToLower(out[0]);
|
boost::algorithm::to_lower(out[0]);
|
||||||
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
||||||
const auto& index = iter128->second;
|
const auto& index = iter128->second;
|
||||||
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
@@ -41,16 +42,13 @@ static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect siz
|
|||||||
|
|
||||||
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
|
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
|
||||||
|
|
||||||
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
|
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, const std::string& name) {
|
||||||
const std::string& name) {
|
const auto upper = boost::algorithm::to_upper_copy(name);
|
||||||
const auto upper = Common::ToUpper(name);
|
|
||||||
|
|
||||||
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
||||||
if (dir->GetFile(fname) != nullptr) {
|
if (dir->GetFile(fname) != nullptr) {
|
||||||
return dir->GetFile(fname);
|
return dir->GetFile(fname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,19 +35,22 @@ 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;
|
||||||
|
|
||||||
auto in_data = in->ReadAllBytes();
|
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
||||||
auto const type = IdentifyMagic(in_data);
|
|
||||||
if (type == IPSFileType::Error)
|
if (type == IPSFileType::Error)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
auto in_data = in->ReadAllBytes();
|
||||||
|
if (in_data.size() == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||||
u64 offset = 5; // After header
|
u64 offset = 5; // After header
|
||||||
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
|
||||||
|
#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"
|
||||||
@@ -72,12 +74,10 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
|
|||||||
#else
|
#else
|
||||||
const auto subdirs = dir->GetSubdirectories();
|
const auto subdirs = dir->GetSubdirectories();
|
||||||
for (const auto& subdir : subdirs) {
|
for (const auto& subdir : subdirs) {
|
||||||
std::string dir_name = Common::ToLower(subdir->GetName());
|
if (name == boost::algorithm::to_lower_copy(subdir->GetName())) {
|
||||||
if (dir_name == name) {
|
|
||||||
return subdir;
|
return subdir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -156,12 +156,15 @@ 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_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||||
|
|
||||||
PatchManager::~PatchManager() = default;
|
PatchManager::~PatchManager() = default;
|
||||||
|
|
||||||
@@ -169,13 +172,35 @@ 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 = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -303,8 +328,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LayeredExeFS
|
// LayeredExeFS
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||||
|
|
||||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
||||||
if (load_dir != nullptr) {
|
if (load_dir != nullptr) {
|
||||||
@@ -346,7 +371,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 = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
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;
|
||||||
@@ -405,7 +430,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 = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
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;
|
||||||
@@ -448,7 +473,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 = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
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;
|
||||||
@@ -462,13 +487,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 = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
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 = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
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(); });
|
||||||
|
|
||||||
@@ -502,17 +527,16 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
|
||||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
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);
|
|
||||||
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 = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
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);
|
||||||
@@ -591,7 +615,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 = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -698,7 +722,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// LayeredFS
|
// LayeredFS
|
||||||
if (apply_layeredfs) {
|
if (apply_layeredfs) {
|
||||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
ApplyLayeredFS(romfs, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return romfs;
|
return romfs;
|
||||||
@@ -1072,15 +1096,22 @@ 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)) {
|
||||||
return content_provider.GetEntryVersion(update_tid);
|
const auto version = content_provider.GetEntryVersion(update_tid);
|
||||||
|
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
return content_provider.GetEntryVersion(title_id);
|
const auto version = 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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,10 +109,14 @@ 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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
// 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 <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
@@ -348,6 +350,22 @@ 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 {
|
||||||
@@ -1397,11 +1415,10 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
|
const auto ext = boost::to_lower_copy(filename.substr(dot_pos + 1));
|
||||||
|
if (ext == "nsp") {
|
||||||
if (extension == "nsp") {
|
|
||||||
ProcessNSP(file);
|
ProcessNSP(file);
|
||||||
} else if (extension == "xci") {
|
} else if (ext == "xci") {
|
||||||
ProcessXCI(file);
|
ProcessXCI(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ 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();
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
|||||||
@@ -109,8 +109,7 @@ 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::shared_ptr<RealVfsFile>(
|
auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path));
|
||||||
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
|
|
||||||
cache[path] = file;
|
cache[path] = file;
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
@@ -177,7 +176,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::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
||||||
@@ -185,7 +184,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
|
|||||||
if (!FS::CreateDirs(path)) {
|
if (!FS::CreateDirs(path)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ 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;
|
||||||
@@ -95,9 +98,6 @@ 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,6 +113,8 @@ 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;
|
||||||
@@ -138,9 +140,6 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
@@ -63,8 +64,8 @@ NAX::NAX(VirtualFile file_)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string two_dir = Common::ToUpper(std::string{match[1]});
|
const std::string two_dir = boost::algorithm::to_upper_copy(std::string{match[1]});
|
||||||
const std::string nca_id = Common::ToLower(std::string{match[2]});
|
const std::string nca_id = boost::algorithm::to_lower_copy(std::string{match[2]});
|
||||||
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -19,8 +22,14 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync wit
|
|||||||
|
|
||||||
// Virtual to Physical core map.
|
// Virtual to Physical core map.
|
||||||
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
||||||
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 1, 2, 3, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
|
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#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 {
|
||||||
@@ -72,10 +73,16 @@ 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,6 +92,13 @@ 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:
|
||||||
|
|||||||
@@ -164,14 +164,14 @@ 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");
|
||||||
|
|
||||||
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
|
||||||
caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel());
|
|
||||||
caller_applet->lifecycle_manager.RequestResumeNotification();
|
|
||||||
caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel());
|
|
||||||
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
|
||||||
}
|
|
||||||
|
|
||||||
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
|
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
|
||||||
|
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
||||||
|
std::scoped_lock lk{caller_applet->lock};
|
||||||
|
const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
||||||
|
const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication();
|
||||||
|
if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel());
|
||||||
|
else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel());
|
||||||
|
}
|
||||||
|
|
||||||
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,7 +122,10 @@ 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;
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{caller_applet->lock};
|
||||||
caller_applet->child_applets.push_back(applet);
|
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);
|
||||||
}
|
}
|
||||||
@@ -148,10 +151,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);
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{caller_applet->lock};
|
||||||
caller_applet->child_applets.push_back(applet);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 = Overlay;
|
z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background;
|
||||||
} else if (inherited_foreground) {
|
} else if (inherited_foreground) {
|
||||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<IAudioInManager>(system), 30);
|
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(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);
|
||||||
|
|||||||
@@ -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, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
|
{551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least)
|
||||||
{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");
|
LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551
|
||||||
|
|
||||||
struct RegisterData {
|
struct RegisterData {
|
||||||
std::array<u8, 0x68> data;
|
std::array<u8, 0x68> data;
|
||||||
|
|||||||
@@ -71,17 +71,14 @@ 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
|
||||||
}
|
}
|
||||||
u8 MemoryRead8(u64 vaddr) override {
|
u64 MemoryRead(u64 vaddr, size_t size) override {
|
||||||
return ReadMemory<u8>(vaddr);
|
switch (size) {
|
||||||
|
case sizeof(u64): return ReadMemory<u64>(vaddr);
|
||||||
|
case sizeof(u32): return ReadMemory<u32>(vaddr);
|
||||||
|
case sizeof(u16): return ReadMemory<u16>(vaddr);
|
||||||
|
case sizeof(u8): return ReadMemory<u8>(vaddr);
|
||||||
|
default: UNREACHABLE();
|
||||||
}
|
}
|
||||||
u16 MemoryRead16(u64 vaddr) override {
|
|
||||||
return ReadMemory<u16>(vaddr);
|
|
||||||
}
|
|
||||||
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);
|
||||||
@@ -89,22 +86,19 @@ public:
|
|||||||
std::string MemoryReadCString(u64 vaddr) {
|
std::string MemoryReadCString(u64 vaddr) {
|
||||||
std::string result{};
|
std::string result{};
|
||||||
u8 next;
|
u8 next;
|
||||||
while ((next = MemoryRead8(vaddr++)) != 0)
|
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
|
||||||
result += char(next);
|
result += char(next);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
|
||||||
WriteMemory<u8>(vaddr, value);
|
switch (size) {
|
||||||
|
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
|
||||||
|
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
|
||||||
|
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
|
||||||
|
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
|
||||||
|
default: UNREACHABLE();
|
||||||
}
|
}
|
||||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
|
||||||
WriteMemory<u16>(vaddr, value);
|
|
||||||
}
|
|
||||||
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);
|
||||||
@@ -193,14 +187,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->MemoryRead32(4)};
|
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
|
||||||
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
|
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != 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->MemoryRead32(mod_offset + 4)};
|
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
|
||||||
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) {
|
||||||
@@ -222,8 +216,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->MemoryRead64(rela.r_offset)};
|
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
|
||||||
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
|
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +225,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->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
|
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
|
||||||
};
|
};
|
||||||
if ((relr & 1) == 0) {
|
if ((relr & 1) == 0) {
|
||||||
// where pointer
|
// where pointer
|
||||||
@@ -294,7 +288,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->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
|
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
|
||||||
jit->SetSP(new_sp);
|
jit->SetSP(new_sp);
|
||||||
}
|
}
|
||||||
// Reset the call state for the next invocation
|
// Reset the call state for the next invocation
|
||||||
@@ -385,17 +379,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++)
|
||||||
MemoryWrite8(dest + i, MemoryRead8(src + i));
|
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = n; i > 0; i--)
|
for (size_t i = n; i > 0; i--)
|
||||||
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
|
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
|
||||||
}
|
}
|
||||||
} 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++)
|
||||||
MemoryWrite8(dest + i, u8(c));
|
MemoryWrite(dest + i, u8(c), sizeof(u8));
|
||||||
} 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))};
|
||||||
@@ -422,7 +416,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 = MemoryRead32(pc);
|
auto const inst = MemoryRead(pc, sizeof(u32));
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
|
|||||||
|
|
||||||
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
||||||
LOG_DEBUG(Service_PSC, "called");
|
LOG_DEBUG(Service_PSC, "called");
|
||||||
*out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system));
|
*out_receiver = std::make_shared<IReceiver>(system);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "common/scm_rev.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/launch_params.h"
|
||||||
|
|
||||||
|
#undef _UNICODE
|
||||||
|
#include <getopt.h>
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept {
|
||||||
|
LaunchParams p{};
|
||||||
|
|
||||||
|
int option_index = 0;
|
||||||
|
static struct option long_options[] = {
|
||||||
|
// clang-format off
|
||||||
|
{"debug", no_argument, 0, 'd'},
|
||||||
|
{"config", required_argument, 0, 'c'},
|
||||||
|
{"fullscreen", no_argument, 0, 'f'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"game", required_argument, 0, 'g'},
|
||||||
|
{"multiplayer", required_argument, 0, 'm'},
|
||||||
|
{"program", optional_argument, 0, 'p'},
|
||||||
|
{"user", required_argument, 0, 'u'},
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
|
{"input-profile", no_argument, 0, 'i'},
|
||||||
|
{"null-render", no_argument, 0, 'n'},
|
||||||
|
{"singlecore", no_argument, 0, 's'},
|
||||||
|
{"filter", no_argument, 0, 'x'},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
|
while (optind < argc) {
|
||||||
|
if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) {
|
||||||
|
switch (char(arg)) {
|
||||||
|
case 'd':
|
||||||
|
p.override_gdb_port = uint16_t(atoi(optarg));
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
p.config_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
p.fullscreen = true;
|
||||||
|
LOG_INFO(Frontend, "Starting in fullscreen mode...");
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
p.print_help = true;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
p.filepath = std::string(optarg);
|
||||||
|
break;
|
||||||
|
case 'i': {
|
||||||
|
p.input_profile = std::string(optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
p.use_multiplayer = true;
|
||||||
|
const std::string str_arg(optarg);
|
||||||
|
// regex to check if the format is nickname:password@ip:port
|
||||||
|
// with optional :password
|
||||||
|
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
|
||||||
|
if (std::regex_match(str_arg, re)) {
|
||||||
|
std::smatch match;
|
||||||
|
std::regex_search(str_arg, match, re);
|
||||||
|
ASSERT(match.size() == 5);
|
||||||
|
p.nickname = match[1];
|
||||||
|
p.password = match[2];
|
||||||
|
p.address = match[3];
|
||||||
|
if (!match[4].str().empty()) {
|
||||||
|
p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
||||||
|
}
|
||||||
|
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
||||||
|
ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
||||||
|
ASSERT(!p.address.empty() && "Address is empty");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p':
|
||||||
|
p.program_args = argv[optind];
|
||||||
|
++optind;
|
||||||
|
break;
|
||||||
|
case 'u': {
|
||||||
|
// Launch game with a specific user
|
||||||
|
bool argument_ok = isdigit(optarg[0]);
|
||||||
|
p.selected_user = atoi(optarg);
|
||||||
|
if (!argument_ok) {
|
||||||
|
// try to look it up by username, only finds the first username that matches.
|
||||||
|
auto const user_idx = system.GetProfileManager().GetUserIndex(optarg);
|
||||||
|
if (user_idx != std::nullopt) {
|
||||||
|
p.selected_user = user_idx.value();
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) {
|
||||||
|
Settings::values.current_user = s32(*p.selected_user);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'v':
|
||||||
|
p.print_version = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
p.force_null_render = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
p.force_single_core = true;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
p.log_filter = argv[optind];
|
||||||
|
++optind;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// only kept due to shortcuts made by Qt frontend which use "-qlaunch"
|
||||||
|
if (strcmp(argv[optind], "-hlaunch") == 0) {
|
||||||
|
p.launch_hlaunch = true;
|
||||||
|
} else if (strcmp(argv[optind], "-qlaunch") == 0) {
|
||||||
|
p.launch_qlaunch = true;
|
||||||
|
} else if (strcmp(argv[optind], "-setup") == 0) {
|
||||||
|
p.launch_setup = true;
|
||||||
|
} else {
|
||||||
|
// qt feeds utf8 data, sdl frontend feeds raw utf16 data
|
||||||
|
#ifdef _WIN32
|
||||||
|
p.filepath = argv_w != nullptr
|
||||||
|
? Common::UTF16ToUTF8(argv_w[optind])
|
||||||
|
: argv[optind];
|
||||||
|
#else
|
||||||
|
p.filepath = argv[optind];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.argv0 = argv[0];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyLaunchParams(LaunchParams const& lp) noexcept {
|
||||||
|
// apply the log_filter setting
|
||||||
|
// the logger was initialized before and doesn't pick up the filter on its own
|
||||||
|
Common::Log::Filter filter{};
|
||||||
|
filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue()));
|
||||||
|
Common::Log::SetGlobalFilter(filter);
|
||||||
|
|
||||||
|
if (!lp.program_args.empty()) {
|
||||||
|
Settings::values.program_args = lp.program_args;
|
||||||
|
}
|
||||||
|
if (!lp.input_profile.empty()) {
|
||||||
|
auto& players = Settings::values.players.GetValue();
|
||||||
|
players[0].profile_name = lp.input_profile;
|
||||||
|
}
|
||||||
|
if (lp.selected_user.has_value()) {
|
||||||
|
Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7);
|
||||||
|
}
|
||||||
|
if (lp.override_gdb_port.has_value()) {
|
||||||
|
Settings::values.use_gdbstub = true;
|
||||||
|
Settings::values.gdbstub_port = *lp.override_gdb_port;
|
||||||
|
}
|
||||||
|
if (lp.force_single_core) {
|
||||||
|
Settings::values.use_multi_core = false;
|
||||||
|
}
|
||||||
|
if (lp.force_null_render) {
|
||||||
|
Settings::values.renderer_backend = Settings::RendererBackend::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp.print_version) {
|
||||||
|
LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc);
|
||||||
|
}
|
||||||
|
if (lp.print_help) {
|
||||||
|
LOG_INFO(Frontend,
|
||||||
|
"Usage: {}"
|
||||||
|
" [options] <filename>\n"
|
||||||
|
"-c, --config Load the specified configuration file\n"
|
||||||
|
"-f, --fullscreen Start in fullscreen mode\n"
|
||||||
|
"-g, --game File path of the game to load\n"
|
||||||
|
"-h, --help Display this help and exit\n"
|
||||||
|
"-m, --multiplayer=nick:password@address:port"
|
||||||
|
" Nickname, password, address and port for multiplayer\n"
|
||||||
|
"-p, --program Pass following string as arguments to executable\n"
|
||||||
|
"-u, --user Select a specific user profile from 0 to 7\n"
|
||||||
|
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
|
||||||
|
"-v, --version Output version information and exit\n",
|
||||||
|
lp.argv0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "network/room.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class System;
|
||||||
|
struct LaunchParams {
|
||||||
|
std::string argv0{};
|
||||||
|
std::string filepath{};
|
||||||
|
std::string nickname{};
|
||||||
|
std::string password{};
|
||||||
|
std::string address{};
|
||||||
|
std::string input_profile{};
|
||||||
|
std::string program_args{};
|
||||||
|
std::optional<std::string> config_path{};
|
||||||
|
std::optional<int> selected_user{};
|
||||||
|
std::optional<u16> override_gdb_port{};
|
||||||
|
std::optional<std::string> log_filter{};
|
||||||
|
u16 port = Network::DefaultRoomPort;
|
||||||
|
bool use_multiplayer = false;
|
||||||
|
bool fullscreen = false;
|
||||||
|
bool force_null_render = false;
|
||||||
|
bool force_single_core = false;
|
||||||
|
// print
|
||||||
|
bool print_version = false;
|
||||||
|
bool print_help = false;
|
||||||
|
// qt
|
||||||
|
bool launch_hlaunch = false;
|
||||||
|
bool launch_qlaunch = false;
|
||||||
|
bool launch_setup = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept;
|
||||||
|
void ApplyLaunchParams(LaunchParams const& lp) noexcept;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include "common/concepts.h"
|
#include "common/concepts.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
@@ -170,8 +171,7 @@ FileType GuessFromFilename(const std::string& name) {
|
|||||||
else if (name == "00")
|
else if (name == "00")
|
||||||
return FileType::NCA;
|
return FileType::NCA;
|
||||||
|
|
||||||
auto const extension =
|
auto const extension = boost::algorithm::to_lower_copy(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||||
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
|
||||||
if (extension == "nro")
|
if (extension == "nro")
|
||||||
return FileType::NRO;
|
return FileType::NRO;
|
||||||
else if (extension == "nso")
|
else if (extension == "nso")
|
||||||
|
|||||||
+31
-4
@@ -7,12 +7,14 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/lz4_compression.h"
|
#include "common/lz4_compression.h"
|
||||||
|
#include "common/zbic_compression.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
@@ -104,11 +106,36 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
|||||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||||
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
|
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
|
||||||
if (nso_header.IsSegmentCompressed(i)) {
|
if (nso_header.IsSegmentCompressed(i)) {
|
||||||
int r = Common::Compression::DecompressDataLZ4(decompressed_size.data(), nso_header.segments[i].size, compressed_data.data(), nso_header.segments_compressed_size[i]);
|
if (nso_header.IsZBICCompressed()) {
|
||||||
ASSERT(r == int(nso_header.segments[i].size));
|
// ZBIC compression
|
||||||
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, decompressed_size.data(), nso_header.segments[i].size);
|
const int r = Common::Compression::DecompressDataZBIC(
|
||||||
|
std::span<u8>{decompressed_size}.first(nso_header.segments[i].size),
|
||||||
|
std::span<const u8>{compressed_data}.first(nso_header.segments_compressed_size[i])
|
||||||
|
);
|
||||||
|
ASSERT(r > 0);
|
||||||
} else {
|
} else {
|
||||||
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, compressed_data.data(), nso_header.segments[i].size);
|
// LZ4 compression
|
||||||
|
int r = Common::Compression::DecompressDataLZ4(
|
||||||
|
decompressed_size.data(),
|
||||||
|
nso_header.segments[i].size,
|
||||||
|
compressed_data.data(),
|
||||||
|
nso_header.segments_compressed_size[i]
|
||||||
|
);
|
||||||
|
ASSERT(r == int(nso_header.segments[i].size));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(
|
||||||
|
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
||||||
|
decompressed_size.data(),
|
||||||
|
nso_header.segments[i].size
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Not compressed
|
||||||
|
std::memcpy(
|
||||||
|
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
||||||
|
compressed_data.data(),
|
||||||
|
nso_header.segments[i].size
|
||||||
|
);
|
||||||
}
|
}
|
||||||
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
|
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
|
||||||
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
|
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
@@ -58,6 +61,9 @@ struct NSOHeader {
|
|||||||
std::array<SHA256Hash, 3> segment_hashes;
|
std::array<SHA256Hash, 3> segment_hashes;
|
||||||
|
|
||||||
bool IsSegmentCompressed(size_t segment_num) const;
|
bool IsSegmentCompressed(size_t segment_num) const;
|
||||||
|
bool IsZBICCompressed() const {
|
||||||
|
return ((flags >> 7) & 1) != 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
||||||
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
||||||
|
|||||||
@@ -33,18 +33,10 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
|
|||||||
|
|
||||||
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
|
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
|
||||||
switch (width) {
|
switch (width) {
|
||||||
case 1:
|
case sizeof(u64): return memory.Write64(addr, value);
|
||||||
memory.Write8(addr, static_cast<u8>(value));
|
case sizeof(u32): return memory.Write32(addr, u32(value));
|
||||||
break;
|
case sizeof(u16): return memory.Write16(addr, u16(value));
|
||||||
case 2:
|
case sizeof(u8): return memory.Write8(addr, u8(value));
|
||||||
memory.Write16(addr, static_cast<u16>(value));
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
memory.Write32(addr, static_cast<u32>(value));
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
memory.Write64(addr, value);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user