summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt11
-rw-r--r--src/common/android/android_common.cpp65
-rw-r--r--src/common/android/android_common.h26
-rw-r--r--src/common/android/applets/software_keyboard.cpp277
-rw-r--r--src/common/android/applets/software_keyboard.h78
-rw-r--r--src/common/android/id_cache.cpp428
-rw-r--r--src/common/android/id_cache.h88
-rw-r--r--src/common/fs/fs_android.cpp167
-rw-r--r--src/common/fs/fs_android.h58
-rw-r--r--src/common/range_sets.h73
-rw-r--r--src/common/range_sets.inc304
-rw-r--r--src/common/settings.cpp3
-rw-r--r--src/common/settings.h32
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/common/slot_vector.h227
16 files changed, 1705 insertions, 135 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 85926fc8f..779be211e 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -107,6 +107,8 @@ add_library(common STATIC
107 quaternion.h 107 quaternion.h
108 range_map.h 108 range_map.h
109 range_mutex.h 109 range_mutex.h
110 range_sets.h
111 range_sets.inc
110 reader_writer_queue.h 112 reader_writer_queue.h
111 ring_buffer.h 113 ring_buffer.h
112 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp 114 ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
@@ -121,6 +123,7 @@ add_library(common STATIC
121 settings_input.cpp 123 settings_input.cpp
122 settings_input.h 124 settings_input.h
123 settings_setting.h 125 settings_setting.h
126 slot_vector.h
124 socket_types.h 127 socket_types.h
125 spin_lock.cpp 128 spin_lock.cpp
126 spin_lock.h 129 spin_lock.h
@@ -179,9 +182,15 @@ endif()
179 182
180if(ANDROID) 183if(ANDROID)
181 target_sources(common 184 target_sources(common
182 PRIVATE 185 PUBLIC
183 fs/fs_android.cpp 186 fs/fs_android.cpp
184 fs/fs_android.h 187 fs/fs_android.h
188 android/android_common.cpp
189 android/android_common.h
190 android/id_cache.cpp
191 android/id_cache.h
192 android/applets/software_keyboard.cpp
193 android/applets/software_keyboard.h
185 ) 194 )
186endif() 195endif()
187 196
diff --git a/src/common/android/android_common.cpp b/src/common/android/android_common.cpp
new file mode 100644
index 000000000..e79005658
--- /dev/null
+++ b/src/common/android/android_common.cpp
@@ -0,0 +1,65 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "android_common.h"
5
6#include <string>
7#include <string_view>
8
9#include <jni.h>
10
11#include "common/android/id_cache.h"
12#include "common/string_util.h"
13
14namespace Common::Android {
15
16std::string GetJString(JNIEnv* env, jstring jstr) {
17 if (!jstr) {
18 return {};
19 }
20
21 const jchar* jchars = env->GetStringChars(jstr, nullptr);
22 const jsize length = env->GetStringLength(jstr);
23 const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars),
24 static_cast<u32>(length));
25 const std::string converted_string = Common::UTF16ToUTF8(string_view);
26 env->ReleaseStringChars(jstr, jchars);
27
28 return converted_string;
29}
30
31jstring ToJString(JNIEnv* env, std::string_view str) {
32 const std::u16string converted_string = Common::UTF8ToUTF16(str);
33 return env->NewString(reinterpret_cast<const jchar*>(converted_string.data()),
34 static_cast<jint>(converted_string.size()));
35}
36
37jstring ToJString(JNIEnv* env, std::u16string_view str) {
38 return ToJString(env, Common::UTF16ToUTF8(str));
39}
40
41double GetJDouble(JNIEnv* env, jobject jdouble) {
42 return env->GetDoubleField(jdouble, GetDoubleValueField());
43}
44
45jobject ToJDouble(JNIEnv* env, double value) {
46 return env->NewObject(GetDoubleClass(), GetDoubleConstructor(), value);
47}
48
49s32 GetJInteger(JNIEnv* env, jobject jinteger) {
50 return env->GetIntField(jinteger, GetIntegerValueField());
51}
52
53jobject ToJInteger(JNIEnv* env, s32 value) {
54 return env->NewObject(GetIntegerClass(), GetIntegerConstructor(), value);
55}
56
57bool GetJBoolean(JNIEnv* env, jobject jboolean) {
58 return env->GetBooleanField(jboolean, GetBooleanValueField());
59}
60
61jobject ToJBoolean(JNIEnv* env, bool value) {
62 return env->NewObject(GetBooleanClass(), GetBooleanConstructor(), value);
63}
64
65} // namespace Common::Android
diff --git a/src/common/android/android_common.h b/src/common/android/android_common.h
new file mode 100644
index 000000000..d0ccb4ec2
--- /dev/null
+++ b/src/common/android/android_common.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include <jni.h>
9#include "common/common_types.h"
10
11namespace Common::Android {
12
13std::string GetJString(JNIEnv* env, jstring jstr);
14jstring ToJString(JNIEnv* env, std::string_view str);
15jstring ToJString(JNIEnv* env, std::u16string_view str);
16
17double GetJDouble(JNIEnv* env, jobject jdouble);
18jobject ToJDouble(JNIEnv* env, double value);
19
20s32 GetJInteger(JNIEnv* env, jobject jinteger);
21jobject ToJInteger(JNIEnv* env, s32 value);
22
23bool GetJBoolean(JNIEnv* env, jobject jboolean);
24jobject ToJBoolean(JNIEnv* env, bool value);
25
26} // namespace Common::Android
diff --git a/src/common/android/applets/software_keyboard.cpp b/src/common/android/applets/software_keyboard.cpp
new file mode 100644
index 000000000..477e62b16
--- /dev/null
+++ b/src/common/android/applets/software_keyboard.cpp
@@ -0,0 +1,277 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <map>
5#include <thread>
6
7#include <jni.h>
8
9#include "common/android/android_common.h"
10#include "common/android/applets/software_keyboard.h"
11#include "common/android/id_cache.h"
12#include "common/logging/log.h"
13#include "common/string_util.h"
14#include "core/core.h"
15
16static jclass s_software_keyboard_class;
17static jclass s_keyboard_config_class;
18static jclass s_keyboard_data_class;
19static jmethodID s_swkbd_execute_normal;
20static jmethodID s_swkbd_execute_inline;
21
22namespace Common::Android::SoftwareKeyboard {
23
24static jobject ToJKeyboardParams(const Core::Frontend::KeyboardInitializeParameters& config) {
25 JNIEnv* env = GetEnvForThread();
26 jobject object = env->AllocObject(s_keyboard_config_class);
27
28 env->SetObjectField(object,
29 env->GetFieldID(s_keyboard_config_class, "ok_text", "Ljava/lang/String;"),
30 ToJString(env, config.ok_text));
31 env->SetObjectField(
32 object, env->GetFieldID(s_keyboard_config_class, "header_text", "Ljava/lang/String;"),
33 ToJString(env, config.header_text));
34 env->SetObjectField(object,
35 env->GetFieldID(s_keyboard_config_class, "sub_text", "Ljava/lang/String;"),
36 ToJString(env, config.sub_text));
37 env->SetObjectField(
38 object, env->GetFieldID(s_keyboard_config_class, "guide_text", "Ljava/lang/String;"),
39 ToJString(env, config.guide_text));
40 env->SetObjectField(
41 object, env->GetFieldID(s_keyboard_config_class, "initial_text", "Ljava/lang/String;"),
42 ToJString(env, config.initial_text));
43 env->SetShortField(object,
44 env->GetFieldID(s_keyboard_config_class, "left_optional_symbol_key", "S"),
45 static_cast<jshort>(config.left_optional_symbol_key));
46 env->SetShortField(object,
47 env->GetFieldID(s_keyboard_config_class, "right_optional_symbol_key", "S"),
48 static_cast<jshort>(config.right_optional_symbol_key));
49 env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "max_text_length", "I"),
50 static_cast<jint>(config.max_text_length));
51 env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "min_text_length", "I"),
52 static_cast<jint>(config.min_text_length));
53 env->SetIntField(object,
54 env->GetFieldID(s_keyboard_config_class, "initial_cursor_position", "I"),
55 static_cast<jint>(config.initial_cursor_position));
56 env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "type", "I"),
57 static_cast<jint>(config.type));
58 env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "password_mode", "I"),
59 static_cast<jint>(config.password_mode));
60 env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "text_draw_type", "I"),
61 static_cast<jint>(config.text_draw_type));
62 env->SetIntField(object, env->GetFieldID(s_keyboard_config_class, "key_disable_flags", "I"),
63 static_cast<jint>(config.key_disable_flags.raw));
64 env->SetBooleanField(object,
65 env->GetFieldID(s_keyboard_config_class, "use_blur_background", "Z"),
66 static_cast<jboolean>(config.use_blur_background));
67 env->SetBooleanField(object,
68 env->GetFieldID(s_keyboard_config_class, "enable_backspace_button", "Z"),
69 static_cast<jboolean>(config.enable_backspace_button));
70 env->SetBooleanField(object,
71 env->GetFieldID(s_keyboard_config_class, "enable_return_button", "Z"),
72 static_cast<jboolean>(config.enable_return_button));
73 env->SetBooleanField(object,
74 env->GetFieldID(s_keyboard_config_class, "disable_cancel_button", "Z"),
75 static_cast<jboolean>(config.disable_cancel_button));
76
77 return object;
78}
79
80AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobject object) {
81 JNIEnv* env = GetEnvForThread();
82 const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
83 object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
84 return ResultData{GetJString(env, string),
85 static_cast<Service::AM::Frontend::SwkbdResult>(env->GetIntField(
86 object, env->GetFieldID(s_keyboard_data_class, "result", "I")))};
87}
88
89AndroidKeyboard::~AndroidKeyboard() = default;
90
91void AndroidKeyboard::InitializeKeyboard(
92 bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
93 SubmitNormalCallback submit_normal_callback_, SubmitInlineCallback submit_inline_callback_) {
94 if (is_inline) {
95 LOG_WARNING(
96 Frontend,
97 "(STUBBED) called, backend requested to initialize the inline software keyboard.");
98
99 submit_inline_callback = std::move(submit_inline_callback_);
100 } else {
101 LOG_WARNING(
102 Frontend,
103 "(STUBBED) called, backend requested to initialize the normal software keyboard.");
104
105 submit_normal_callback = std::move(submit_normal_callback_);
106 }
107
108 parameters = std::move(initialize_parameters);
109
110 LOG_INFO(Frontend,
111 "\nKeyboardInitializeParameters:"
112 "\nok_text={}"
113 "\nheader_text={}"
114 "\nsub_text={}"
115 "\nguide_text={}"
116 "\ninitial_text={}"
117 "\nmax_text_length={}"
118 "\nmin_text_length={}"
119 "\ninitial_cursor_position={}"
120 "\ntype={}"
121 "\npassword_mode={}"
122 "\ntext_draw_type={}"
123 "\nkey_disable_flags={}"
124 "\nuse_blur_background={}"
125 "\nenable_backspace_button={}"
126 "\nenable_return_button={}"
127 "\ndisable_cancel_button={}",
128 Common::UTF16ToUTF8(parameters.ok_text), Common::UTF16ToUTF8(parameters.header_text),
129 Common::UTF16ToUTF8(parameters.sub_text), Common::UTF16ToUTF8(parameters.guide_text),
130 Common::UTF16ToUTF8(parameters.initial_text), parameters.max_text_length,
131 parameters.min_text_length, parameters.initial_cursor_position, parameters.type,
132 parameters.password_mode, parameters.text_draw_type, parameters.key_disable_flags.raw,
133 parameters.use_blur_background, parameters.enable_backspace_button,
134 parameters.enable_return_button, parameters.disable_cancel_button);
135}
136
137void AndroidKeyboard::ShowNormalKeyboard() const {
138 LOG_DEBUG(Frontend, "called, backend requested to show the normal software keyboard.");
139
140 ResultData data{};
141
142 // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
143 std::thread([&] {
144 data = ResultData::CreateFromFrontend(GetEnvForThread()->CallStaticObjectMethod(
145 s_software_keyboard_class, s_swkbd_execute_normal, ToJKeyboardParams(parameters)));
146 }).join();
147
148 SubmitNormalText(data);
149}
150
151void AndroidKeyboard::ShowTextCheckDialog(
152 Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
153 std::u16string text_check_message) const {
154 LOG_WARNING(Frontend, "(STUBBED) called, backend requested to show the text check dialog.");
155}
156
157void AndroidKeyboard::ShowInlineKeyboard(
158 Core::Frontend::InlineAppearParameters appear_parameters) const {
159 LOG_WARNING(Frontend,
160 "(STUBBED) called, backend requested to show the inline software keyboard.");
161
162 LOG_INFO(Frontend,
163 "\nInlineAppearParameters:"
164 "\nmax_text_length={}"
165 "\nmin_text_length={}"
166 "\nkey_top_scale_x={}"
167 "\nkey_top_scale_y={}"
168 "\nkey_top_translate_x={}"
169 "\nkey_top_translate_y={}"
170 "\ntype={}"
171 "\nkey_disable_flags={}"
172 "\nkey_top_as_floating={}"
173 "\nenable_backspace_button={}"
174 "\nenable_return_button={}"
175 "\ndisable_cancel_button={}",
176 appear_parameters.max_text_length, appear_parameters.min_text_length,
177 appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y,
178 appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y,
179 appear_parameters.type, appear_parameters.key_disable_flags.raw,
180 appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button,
181 appear_parameters.enable_return_button, appear_parameters.disable_cancel_button);
182
183 // Pivot to a new thread, as we cannot call GetEnvForThread() from a Fiber.
184 m_is_inline_active = true;
185 std::thread([&] {
186 GetEnvForThread()->CallStaticVoidMethod(s_software_keyboard_class, s_swkbd_execute_inline,
187 ToJKeyboardParams(parameters));
188 }).join();
189}
190
191void AndroidKeyboard::HideInlineKeyboard() const {
192 LOG_WARNING(Frontend,
193 "(STUBBED) called, backend requested to hide the inline software keyboard.");
194}
195
196void AndroidKeyboard::InlineTextChanged(
197 Core::Frontend::InlineTextParameters text_parameters) const {
198 LOG_WARNING(Frontend,
199 "(STUBBED) called, backend requested to change the inline keyboard text.");
200
201 LOG_INFO(Frontend,
202 "\nInlineTextParameters:"
203 "\ninput_text={}"
204 "\ncursor_position={}",
205 Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
206
207 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
208 text_parameters.input_text, text_parameters.cursor_position);
209}
210
211void AndroidKeyboard::ExitKeyboard() const {
212 LOG_WARNING(Frontend, "(STUBBED) called, backend requested to exit the software keyboard.");
213}
214
215void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
216 if (!m_is_inline_active) {
217 return;
218 }
219
220 m_current_text += submitted_text;
221
222 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
223 static_cast<int>(m_current_text.size()));
224}
225
226void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
227 static constexpr int KEYCODE_BACK = 4;
228 static constexpr int KEYCODE_ENTER = 66;
229 static constexpr int KEYCODE_DEL = 67;
230
231 if (!m_is_inline_active) {
232 return;
233 }
234
235 switch (key_code) {
236 case KEYCODE_BACK:
237 case KEYCODE_ENTER:
238 m_is_inline_active = false;
239 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter, m_current_text,
240 static_cast<s32>(m_current_text.size()));
241 break;
242 case KEYCODE_DEL:
243 m_current_text.pop_back();
244 submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
245 static_cast<int>(m_current_text.size()));
246 break;
247 }
248}
249
250void AndroidKeyboard::SubmitNormalText(const ResultData& data) const {
251 submit_normal_callback(data.result, Common::UTF8ToUTF16(data.text), true);
252}
253
254void InitJNI(JNIEnv* env) {
255 s_software_keyboard_class = reinterpret_cast<jclass>(
256 env->NewGlobalRef(env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard")));
257 s_keyboard_config_class = reinterpret_cast<jclass>(env->NewGlobalRef(
258 env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig")));
259 s_keyboard_data_class = reinterpret_cast<jclass>(env->NewGlobalRef(
260 env->FindClass("org/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardData")));
261
262 s_swkbd_execute_normal = env->GetStaticMethodID(
263 s_software_keyboard_class, "executeNormal",
264 "(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)Lorg/yuzu/yuzu_emu/"
265 "applets/keyboard/SoftwareKeyboard$KeyboardData;");
266 s_swkbd_execute_inline = env->GetStaticMethodID(
267 s_software_keyboard_class, "executeInline",
268 "(Lorg/yuzu/yuzu_emu/applets/keyboard/SoftwareKeyboard$KeyboardConfig;)V");
269}
270
271void CleanupJNI(JNIEnv* env) {
272 env->DeleteGlobalRef(s_software_keyboard_class);
273 env->DeleteGlobalRef(s_keyboard_config_class);
274 env->DeleteGlobalRef(s_keyboard_data_class);
275}
276
277} // namespace Common::Android::SoftwareKeyboard
diff --git a/src/common/android/applets/software_keyboard.h b/src/common/android/applets/software_keyboard.h
new file mode 100644
index 000000000..9fd09d27c
--- /dev/null
+++ b/src/common/android/applets/software_keyboard.h
@@ -0,0 +1,78 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <jni.h>
7
8#include "core/frontend/applets/software_keyboard.h"
9
10namespace Common::Android::SoftwareKeyboard {
11
12class AndroidKeyboard final : public Core::Frontend::SoftwareKeyboardApplet {
13public:
14 ~AndroidKeyboard() override;
15
16 void Close() const override {
17 ExitKeyboard();
18 }
19
20 void InitializeKeyboard(bool is_inline,
21 Core::Frontend::KeyboardInitializeParameters initialize_parameters,
22 SubmitNormalCallback submit_normal_callback_,
23 SubmitInlineCallback submit_inline_callback_) override;
24
25 void ShowNormalKeyboard() const override;
26
27 void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
28 std::u16string text_check_message) const override;
29
30 void ShowInlineKeyboard(
31 Core::Frontend::InlineAppearParameters appear_parameters) const override;
32
33 void HideInlineKeyboard() const override;
34
35 void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override;
36
37 void ExitKeyboard() const override;
38
39 void SubmitInlineKeyboardText(std::u16string submitted_text);
40
41 void SubmitInlineKeyboardInput(int key_code);
42
43private:
44 struct ResultData {
45 static ResultData CreateFromFrontend(jobject object);
46
47 std::string text;
48 Service::AM::Frontend::SwkbdResult result{};
49 };
50
51 void SubmitNormalText(const ResultData& result) const;
52
53 Core::Frontend::KeyboardInitializeParameters parameters{};
54
55 mutable SubmitNormalCallback submit_normal_callback;
56 mutable SubmitInlineCallback submit_inline_callback;
57
58private:
59 mutable bool m_is_inline_active{};
60 std::u16string m_current_text;
61};
62
63// Should be called in JNI_Load
64void InitJNI(JNIEnv* env);
65
66// Should be called in JNI_Unload
67void CleanupJNI(JNIEnv* env);
68
69} // namespace Common::Android::SoftwareKeyboard
70
71// Native function calls
72extern "C" {
73JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_applets_SoftwareKeyboard_ValidateFilters(
74 JNIEnv* env, jclass clazz, jstring text);
75
76JNIEXPORT jobject JNICALL Java_org_citra_citra_1emu_applets_SoftwareKeyboard_ValidateInput(
77 JNIEnv* env, jclass clazz, jstring text);
78}
diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp
new file mode 100644
index 000000000..f39262db9
--- /dev/null
+++ b/src/common/android/id_cache.cpp
@@ -0,0 +1,428 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <jni.h>
5
6#include "applets/software_keyboard.h"
7#include "common/android/id_cache.h"
8#include "common/assert.h"
9#include "common/fs/fs_android.h"
10#include "video_core/rasterizer_interface.h"
11
12static JavaVM* s_java_vm;
13static jclass s_native_library_class;
14static jclass s_disk_cache_progress_class;
15static jclass s_load_callback_stage_class;
16static jclass s_game_dir_class;
17static jmethodID s_game_dir_constructor;
18static jmethodID s_exit_emulation_activity;
19static jmethodID s_disk_cache_load_progress;
20static jmethodID s_on_emulation_started;
21static jmethodID s_on_emulation_stopped;
22static jmethodID s_on_program_changed;
23
24static jclass s_game_class;
25static jmethodID s_game_constructor;
26static jfieldID s_game_title_field;
27static jfieldID s_game_path_field;
28static jfieldID s_game_program_id_field;
29static jfieldID s_game_developer_field;
30static jfieldID s_game_version_field;
31static jfieldID s_game_is_homebrew_field;
32
33static jclass s_string_class;
34static jclass s_pair_class;
35static jmethodID s_pair_constructor;
36static jfieldID s_pair_first_field;
37static jfieldID s_pair_second_field;
38
39static jclass s_overlay_control_data_class;
40static jmethodID s_overlay_control_data_constructor;
41static jfieldID s_overlay_control_data_id_field;
42static jfieldID s_overlay_control_data_enabled_field;
43static jfieldID s_overlay_control_data_landscape_position_field;
44static jfieldID s_overlay_control_data_portrait_position_field;
45static jfieldID s_overlay_control_data_foldable_position_field;
46
47static jclass s_patch_class;
48static jmethodID s_patch_constructor;
49static jfieldID s_patch_enabled_field;
50static jfieldID s_patch_name_field;
51static jfieldID s_patch_version_field;
52static jfieldID s_patch_type_field;
53static jfieldID s_patch_program_id_field;
54static jfieldID s_patch_title_id_field;
55
56static jclass s_double_class;
57static jmethodID s_double_constructor;
58static jfieldID s_double_value_field;
59
60static jclass s_integer_class;
61static jmethodID s_integer_constructor;
62static jfieldID s_integer_value_field;
63
64static jclass s_boolean_class;
65static jmethodID s_boolean_constructor;
66static jfieldID s_boolean_value_field;
67
68static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
69
70namespace Common::Android {
71
72JNIEnv* GetEnvForThread() {
73 thread_local static struct OwnedEnv {
74 OwnedEnv() {
75 status = s_java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
76 if (status == JNI_EDETACHED)
77 s_java_vm->AttachCurrentThread(&env, nullptr);
78 }
79
80 ~OwnedEnv() {
81 if (status == JNI_EDETACHED)
82 s_java_vm->DetachCurrentThread();
83 }
84
85 int status;
86 JNIEnv* env = nullptr;
87 } owned;
88 return owned.env;
89}
90
91jclass GetNativeLibraryClass() {
92 return s_native_library_class;
93}
94
95jclass GetDiskCacheProgressClass() {
96 return s_disk_cache_progress_class;
97}
98
99jclass GetDiskCacheLoadCallbackStageClass() {
100 return s_load_callback_stage_class;
101}
102
103jclass GetGameDirClass() {
104 return s_game_dir_class;
105}
106
107jmethodID GetGameDirConstructor() {
108 return s_game_dir_constructor;
109}
110
111jmethodID GetExitEmulationActivity() {
112 return s_exit_emulation_activity;
113}
114
115jmethodID GetDiskCacheLoadProgress() {
116 return s_disk_cache_load_progress;
117}
118
119jmethodID GetOnEmulationStarted() {
120 return s_on_emulation_started;
121}
122
123jmethodID GetOnEmulationStopped() {
124 return s_on_emulation_stopped;
125}
126
127jmethodID GetOnProgramChanged() {
128 return s_on_program_changed;
129}
130
131jclass GetGameClass() {
132 return s_game_class;
133}
134
135jmethodID GetGameConstructor() {
136 return s_game_constructor;
137}
138
139jfieldID GetGameTitleField() {
140 return s_game_title_field;
141}
142
143jfieldID GetGamePathField() {
144 return s_game_path_field;
145}
146
147jfieldID GetGameProgramIdField() {
148 return s_game_program_id_field;
149}
150
151jfieldID GetGameDeveloperField() {
152 return s_game_developer_field;
153}
154
155jfieldID GetGameVersionField() {
156 return s_game_version_field;
157}
158
159jfieldID GetGameIsHomebrewField() {
160 return s_game_is_homebrew_field;
161}
162
163jclass GetStringClass() {
164 return s_string_class;
165}
166
167jclass GetPairClass() {
168 return s_pair_class;
169}
170
171jmethodID GetPairConstructor() {
172 return s_pair_constructor;
173}
174
175jfieldID GetPairFirstField() {
176 return s_pair_first_field;
177}
178
179jfieldID GetPairSecondField() {
180 return s_pair_second_field;
181}
182
183jclass GetOverlayControlDataClass() {
184 return s_overlay_control_data_class;
185}
186
187jmethodID GetOverlayControlDataConstructor() {
188 return s_overlay_control_data_constructor;
189}
190
191jfieldID GetOverlayControlDataIdField() {
192 return s_overlay_control_data_id_field;
193}
194
195jfieldID GetOverlayControlDataEnabledField() {
196 return s_overlay_control_data_enabled_field;
197}
198
199jfieldID GetOverlayControlDataLandscapePositionField() {
200 return s_overlay_control_data_landscape_position_field;
201}
202
203jfieldID GetOverlayControlDataPortraitPositionField() {
204 return s_overlay_control_data_portrait_position_field;
205}
206
207jfieldID GetOverlayControlDataFoldablePositionField() {
208 return s_overlay_control_data_foldable_position_field;
209}
210
211jclass GetPatchClass() {
212 return s_patch_class;
213}
214
215jmethodID GetPatchConstructor() {
216 return s_patch_constructor;
217}
218
219jfieldID GetPatchEnabledField() {
220 return s_patch_enabled_field;
221}
222
223jfieldID GetPatchNameField() {
224 return s_patch_name_field;
225}
226
227jfieldID GetPatchVersionField() {
228 return s_patch_version_field;
229}
230
231jfieldID GetPatchTypeField() {
232 return s_patch_type_field;
233}
234
235jfieldID GetPatchProgramIdField() {
236 return s_patch_program_id_field;
237}
238
239jfieldID GetPatchTitleIdField() {
240 return s_patch_title_id_field;
241}
242
243jclass GetDoubleClass() {
244 return s_double_class;
245}
246
247jmethodID GetDoubleConstructor() {
248 return s_double_constructor;
249}
250
251jfieldID GetDoubleValueField() {
252 return s_double_value_field;
253}
254
255jclass GetIntegerClass() {
256 return s_integer_class;
257}
258
259jmethodID GetIntegerConstructor() {
260 return s_integer_constructor;
261}
262
263jfieldID GetIntegerValueField() {
264 return s_integer_value_field;
265}
266
267jclass GetBooleanClass() {
268 return s_boolean_class;
269}
270
271jmethodID GetBooleanConstructor() {
272 return s_boolean_constructor;
273}
274
275jfieldID GetBooleanValueField() {
276 return s_boolean_value_field;
277}
278
279#ifdef __cplusplus
280extern "C" {
281#endif
282
283jint JNI_OnLoad(JavaVM* vm, void* reserved) {
284 s_java_vm = vm;
285
286 JNIEnv* env;
287 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK)
288 return JNI_ERR;
289
290 // Initialize Java classes
291 const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
292 s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
293 s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
294 env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
295 s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
296 "org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
297
298 const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
299 s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
300 s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>", "(Ljava/lang/String;Z)V");
301 env->DeleteLocalRef(game_dir_class);
302
303 // Initialize methods
304 s_exit_emulation_activity =
305 env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
306 s_disk_cache_load_progress =
307 env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
308 s_on_emulation_started =
309 env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
310 s_on_emulation_stopped =
311 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
312 s_on_program_changed =
313 env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
314
315 const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
316 s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
317 s_game_constructor = env->GetMethodID(game_class, "<init>",
318 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
319 "String;Ljava/lang/String;Ljava/lang/String;Z)V");
320 s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
321 s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
322 s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
323 s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
324 s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
325 s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
326 env->DeleteLocalRef(game_class);
327
328 const jclass string_class = env->FindClass("java/lang/String");
329 s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
330 env->DeleteLocalRef(string_class);
331
332 const jclass pair_class = env->FindClass("kotlin/Pair");
333 s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
334 s_pair_constructor =
335 env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
336 s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
337 s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
338 env->DeleteLocalRef(pair_class);
339
340 const jclass overlay_control_data_class =
341 env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
342 s_overlay_control_data_class =
343 reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
344 s_overlay_control_data_constructor =
345 env->GetMethodID(overlay_control_data_class, "<init>",
346 "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
347 s_overlay_control_data_id_field =
348 env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
349 s_overlay_control_data_enabled_field =
350 env->GetFieldID(overlay_control_data_class, "enabled", "Z");
351 s_overlay_control_data_landscape_position_field =
352 env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
353 s_overlay_control_data_portrait_position_field =
354 env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
355 s_overlay_control_data_foldable_position_field =
356 env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
357 env->DeleteLocalRef(overlay_control_data_class);
358
359 const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
360 s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
361 s_patch_constructor = env->GetMethodID(
362 patch_class, "<init>",
363 "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V");
364 s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
365 s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
366 s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
367 s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
368 s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
369 s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
370 env->DeleteLocalRef(patch_class);
371
372 const jclass double_class = env->FindClass("java/lang/Double");
373 s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
374 s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
375 s_double_value_field = env->GetFieldID(double_class, "value", "D");
376 env->DeleteLocalRef(double_class);
377
378 const jclass int_class = env->FindClass("java/lang/Integer");
379 s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
380 s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
381 s_integer_value_field = env->GetFieldID(int_class, "value", "I");
382 env->DeleteLocalRef(int_class);
383
384 const jclass boolean_class = env->FindClass("java/lang/Boolean");
385 s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
386 s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
387 s_boolean_value_field = env->GetFieldID(boolean_class, "value", "Z");
388 env->DeleteLocalRef(boolean_class);
389
390 // Initialize Android Storage
391 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
392
393 // Initialize applets
394 Common::Android::SoftwareKeyboard::InitJNI(env);
395
396 return JNI_VERSION;
397}
398
399void JNI_OnUnload(JavaVM* vm, void* reserved) {
400 JNIEnv* env;
401 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) {
402 return;
403 }
404
405 // UnInitialize Android Storage
406 Common::FS::Android::UnRegisterCallbacks();
407 env->DeleteGlobalRef(s_native_library_class);
408 env->DeleteGlobalRef(s_disk_cache_progress_class);
409 env->DeleteGlobalRef(s_load_callback_stage_class);
410 env->DeleteGlobalRef(s_game_dir_class);
411 env->DeleteGlobalRef(s_game_class);
412 env->DeleteGlobalRef(s_string_class);
413 env->DeleteGlobalRef(s_pair_class);
414 env->DeleteGlobalRef(s_overlay_control_data_class);
415 env->DeleteGlobalRef(s_patch_class);
416 env->DeleteGlobalRef(s_double_class);
417 env->DeleteGlobalRef(s_integer_class);
418 env->DeleteGlobalRef(s_boolean_class);
419
420 // UnInitialize applets
421 SoftwareKeyboard::CleanupJNI(env);
422}
423
424#ifdef __cplusplus
425}
426#endif
427
428} // namespace Common::Android
diff --git a/src/common/android/id_cache.h b/src/common/android/id_cache.h
new file mode 100644
index 000000000..47802f96c
--- /dev/null
+++ b/src/common/android/id_cache.h
@@ -0,0 +1,88 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <future>
7#include <jni.h>
8
9#include "video_core/rasterizer_interface.h"
10
11namespace Common::Android {
12
13JNIEnv* GetEnvForThread();
14
15/**
16 * Starts a new thread to run JNI. Intended to be used when you must run JNI from a fiber.
17 * @tparam T Typename of the return value for the work param
18 * @param work Lambda that runs JNI code. This function will take care of attaching this thread to
19 * the JVM
20 * @return The result from the work lambda param
21 */
22template <typename T = void>
23T RunJNIOnFiber(const std::function<T(JNIEnv*)>& work) {
24 std::future<T> j_result = std::async(std::launch::async, [&] {
25 auto env = GetEnvForThread();
26 return work(env);
27 });
28 return j_result.get();
29}
30
31jclass GetNativeLibraryClass();
32
33jclass GetDiskCacheProgressClass();
34jclass GetDiskCacheLoadCallbackStageClass();
35jclass GetGameDirClass();
36jmethodID GetGameDirConstructor();
37jmethodID GetDiskCacheLoadProgress();
38
39jmethodID GetExitEmulationActivity();
40jmethodID GetOnEmulationStarted();
41jmethodID GetOnEmulationStopped();
42jmethodID GetOnProgramChanged();
43
44jclass GetGameClass();
45jmethodID GetGameConstructor();
46jfieldID GetGameTitleField();
47jfieldID GetGamePathField();
48jfieldID GetGameProgramIdField();
49jfieldID GetGameDeveloperField();
50jfieldID GetGameVersionField();
51jfieldID GetGameIsHomebrewField();
52
53jclass GetStringClass();
54jclass GetPairClass();
55jmethodID GetPairConstructor();
56jfieldID GetPairFirstField();
57jfieldID GetPairSecondField();
58
59jclass GetOverlayControlDataClass();
60jmethodID GetOverlayControlDataConstructor();
61jfieldID GetOverlayControlDataIdField();
62jfieldID GetOverlayControlDataEnabledField();
63jfieldID GetOverlayControlDataLandscapePositionField();
64jfieldID GetOverlayControlDataPortraitPositionField();
65jfieldID GetOverlayControlDataFoldablePositionField();
66
67jclass GetPatchClass();
68jmethodID GetPatchConstructor();
69jfieldID GetPatchEnabledField();
70jfieldID GetPatchNameField();
71jfieldID GetPatchVersionField();
72jfieldID GetPatchTypeField();
73jfieldID GetPatchProgramIdField();
74jfieldID GetPatchTitleIdField();
75
76jclass GetDoubleClass();
77jmethodID GetDoubleConstructor();
78jfieldID GetDoubleValueField();
79
80jclass GetIntegerClass();
81jmethodID GetIntegerConstructor();
82jfieldID GetIntegerValueField();
83
84jclass GetBooleanClass();
85jmethodID GetBooleanConstructor();
86jfieldID GetBooleanValueField();
87
88} // namespace Common::Android
diff --git a/src/common/fs/fs_android.cpp b/src/common/fs/fs_android.cpp
index 1dd826a4a..9a8053222 100644
--- a/src/common/fs/fs_android.cpp
+++ b/src/common/fs/fs_android.cpp
@@ -1,63 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/android/android_common.h"
5#include "common/android/id_cache.h"
6#include "common/assert.h"
4#include "common/fs/fs_android.h" 7#include "common/fs/fs_android.h"
5#include "common/string_util.h" 8#include "common/string_util.h"
6 9
7namespace Common::FS::Android { 10namespace Common::FS::Android {
8 11
9JNIEnv* GetEnvForThread() {
10 thread_local static struct OwnedEnv {
11 OwnedEnv() {
12 status = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
13 if (status == JNI_EDETACHED)
14 g_jvm->AttachCurrentThread(&env, nullptr);
15 }
16
17 ~OwnedEnv() {
18 if (status == JNI_EDETACHED)
19 g_jvm->DetachCurrentThread();
20 }
21
22 int status;
23 JNIEnv* env = nullptr;
24 } owned;
25 return owned.env;
26}
27
28void RegisterCallbacks(JNIEnv* env, jclass clazz) { 12void RegisterCallbacks(JNIEnv* env, jclass clazz) {
29 env->GetJavaVM(&g_jvm); 13 env->GetJavaVM(&g_jvm);
30 native_library = clazz; 14 native_library = clazz;
31 15
32#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ 16 s_get_parent_directory = env->GetStaticMethodID(native_library, "getParentDirectory",
33 F(JMethodID, JMethodName, Signature) 17 "(Ljava/lang/String;)Ljava/lang/String;");
34#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ 18 s_get_filename = env->GetStaticMethodID(native_library, "getFilename",
35 F(JMethodID, JMethodName, Signature) 19 "(Ljava/lang/String;)Ljava/lang/String;");
36#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ 20 s_get_size = env->GetStaticMethodID(native_library, "getSize", "(Ljava/lang/String;)J");
37 F(JMethodID, JMethodName, Signature) 21 s_is_directory = env->GetStaticMethodID(native_library, "isDirectory", "(Ljava/lang/String;)Z");
38#define F(JMethodID, JMethodName, Signature) \ 22 s_file_exists = env->GetStaticMethodID(native_library, "exists", "(Ljava/lang/String;)Z");
39 JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature); 23 s_open_content_uri = env->GetStaticMethodID(native_library, "openContentUri",
40 ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 24 "(Ljava/lang/String;Ljava/lang/String;)I");
41 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
42 ANDROID_STORAGE_FUNCTIONS(FS)
43#undef F
44#undef FS
45#undef FR
46#undef FH
47} 25}
48 26
49void UnRegisterCallbacks() { 27void UnRegisterCallbacks() {
50#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 28 s_get_parent_directory = nullptr;
51#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 29 s_get_filename = nullptr;
52#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) 30
53#define F(JMethodID) JMethodID = nullptr; 31 s_get_size = nullptr;
54 ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 32 s_is_directory = nullptr;
55 ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 33 s_file_exists = nullptr;
56 ANDROID_STORAGE_FUNCTIONS(FS) 34
57#undef F 35 s_open_content_uri = nullptr;
58#undef FS
59#undef FR
60#undef FH
61} 36}
62 37
63bool IsContentUri(const std::string& path) { 38bool IsContentUri(const std::string& path) {
@@ -69,8 +44,8 @@ bool IsContentUri(const std::string& path) {
69 return path.find(prefix) == 0; 44 return path.find(prefix) == 0;
70} 45}
71 46
72int OpenContentUri(const std::string& filepath, OpenMode openmode) { 47s32 OpenContentUri(const std::string& filepath, OpenMode openmode) {
73 if (open_content_uri == nullptr) 48 if (s_open_content_uri == nullptr)
74 return -1; 49 return -1;
75 50
76 const char* mode = ""; 51 const char* mode = "";
@@ -82,50 +57,66 @@ int OpenContentUri(const std::string& filepath, OpenMode openmode) {
82 UNIMPLEMENTED(); 57 UNIMPLEMENTED();
83 return -1; 58 return -1;
84 } 59 }
85 auto env = GetEnvForThread(); 60 auto env = Common::Android::GetEnvForThread();
86 jstring j_filepath = env->NewStringUTF(filepath.c_str()); 61 jstring j_filepath = Common::Android::ToJString(env, filepath);
87 jstring j_mode = env->NewStringUTF(mode); 62 jstring j_mode = Common::Android::ToJString(env, mode);
88 return env->CallStaticIntMethod(native_library, open_content_uri, j_filepath, j_mode); 63 return env->CallStaticIntMethod(native_library, s_open_content_uri, j_filepath, j_mode);
64}
65
66u64 GetSize(const std::string& filepath) {
67 if (s_get_size == nullptr) {
68 return 0;
69 }
70 auto env = Common::Android::GetEnvForThread();
71 return static_cast<u64>(env->CallStaticLongMethod(
72 native_library, s_get_size,
73 Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath)));
74}
75
76bool IsDirectory(const std::string& filepath) {
77 if (s_is_directory == nullptr) {
78 return 0;
79 }
80 auto env = Common::Android::GetEnvForThread();
81 return env->CallStaticBooleanMethod(
82 native_library, s_is_directory,
83 Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
89} 84}
90 85
91#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \ 86bool Exists(const std::string& filepath) {
92 F(FunctionName, ReturnValue, JMethodID, Caller) 87 if (s_file_exists == nullptr) {
93#define F(FunctionName, ReturnValue, JMethodID, Caller) \ 88 return 0;
94 ReturnValue FunctionName(const std::string& filepath) { \
95 if (JMethodID == nullptr) { \
96 return 0; \
97 } \
98 auto env = GetEnvForThread(); \
99 jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
100 return env->Caller(native_library, JMethodID, j_filepath); \
101 } 89 }
102ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 90 auto env = Common::Android::GetEnvForThread();
103#undef F 91 return env->CallStaticBooleanMethod(
104#undef FR 92 native_library, s_file_exists,
105 93 Common::Android::ToJString(Common::Android::GetEnvForThread(), filepath));
106#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \ 94}
107 F(FunctionName, JMethodID, Caller) 95
108#define F(FunctionName, JMethodID, Caller) \ 96std::string GetParentDirectory(const std::string& filepath) {
109 std::string FunctionName(const std::string& filepath) { \ 97 if (s_get_parent_directory == nullptr) {
110 if (JMethodID == nullptr) { \ 98 return 0;
111 return 0; \
112 } \
113 auto env = GetEnvForThread(); \
114 jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
115 jstring j_return = \
116 static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
117 if (!j_return) { \
118 return {}; \
119 } \
120 const jchar* jchars = env->GetStringChars(j_return, nullptr); \
121 const jsize length = env->GetStringLength(j_return); \
122 const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
123 const std::string converted_string = Common::UTF16ToUTF8(string_view); \
124 env->ReleaseStringChars(j_return, jchars); \
125 return converted_string; \
126 } 99 }
127ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 100 auto env = Common::Android::GetEnvForThread();
128#undef F 101 jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
129#undef FH 102 native_library, s_get_parent_directory, Common::Android::ToJString(env, filepath)));
103 if (!j_return) {
104 return {};
105 }
106 return Common::Android::GetJString(env, j_return);
107}
108
109std::string GetFilename(const std::string& filepath) {
110 if (s_get_filename == nullptr) {
111 return 0;
112 }
113 auto env = Common::Android::GetEnvForThread();
114 jstring j_return = static_cast<jstring>(env->CallStaticObjectMethod(
115 native_library, s_get_filename, Common::Android::ToJString(env, filepath)));
116 if (!j_return) {
117 return {};
118 }
119 return Common::Android::GetJString(env, j_return);
120}
130 121
131} // namespace Common::FS::Android 122} // namespace Common::FS::Android
diff --git a/src/common/fs/fs_android.h b/src/common/fs/fs_android.h
index 2c9234313..b33f4beb8 100644
--- a/src/common/fs/fs_android.h
+++ b/src/common/fs/fs_android.h
@@ -7,38 +7,17 @@
7#include <vector> 7#include <vector>
8#include <jni.h> 8#include <jni.h>
9 9
10#define ANDROID_STORAGE_FUNCTIONS(V) \
11 V(OpenContentUri, int, (const std::string& filepath, OpenMode openmode), open_content_uri, \
12 "openContentUri", "(Ljava/lang/String;Ljava/lang/String;)I")
13
14#define ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(V) \
15 V(GetSize, std::uint64_t, get_size, CallStaticLongMethod, "getSize", "(Ljava/lang/String;)J") \
16 V(IsDirectory, bool, is_directory, CallStaticBooleanMethod, "isDirectory", \
17 "(Ljava/lang/String;)Z") \
18 V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
19
20#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
21 V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
22 "(Ljava/lang/String;)Ljava/lang/String;") \
23 V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
24 "(Ljava/lang/String;)Ljava/lang/String;")
25
26namespace Common::FS::Android { 10namespace Common::FS::Android {
27 11
28static JavaVM* g_jvm = nullptr; 12static JavaVM* g_jvm = nullptr;
29static jclass native_library = nullptr; 13static jclass native_library = nullptr;
30 14
31#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 15static jmethodID s_get_parent_directory;
32#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID) 16static jmethodID s_get_filename;
33#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID) 17static jmethodID s_get_size;
34#define F(JMethodID) static jmethodID JMethodID = nullptr; 18static jmethodID s_is_directory;
35ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH) 19static jmethodID s_file_exists;
36ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR) 20static jmethodID s_open_content_uri;
37ANDROID_STORAGE_FUNCTIONS(FS)
38#undef F
39#undef FS
40#undef FR
41#undef FH
42 21
43enum class OpenMode { 22enum class OpenMode {
44 Read, 23 Read,
@@ -57,24 +36,11 @@ void UnRegisterCallbacks();
57 36
58bool IsContentUri(const std::string& path); 37bool IsContentUri(const std::string& path);
59 38
60#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \ 39int OpenContentUri(const std::string& filepath, OpenMode openmode);
61 F(FunctionName, Parameters, ReturnValue) 40std::uint64_t GetSize(const std::string& filepath);
62#define F(FunctionName, Parameters, ReturnValue) ReturnValue FunctionName Parameters; 41bool IsDirectory(const std::string& filepath);
63ANDROID_STORAGE_FUNCTIONS(FS) 42bool Exists(const std::string& filepath);
64#undef F 43std::string GetParentDirectory(const std::string& filepath);
65#undef FS 44std::string GetFilename(const std::string& filepath);
66
67#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
68 F(FunctionName, ReturnValue)
69#define F(FunctionName, ReturnValue) ReturnValue FunctionName(const std::string& filepath);
70ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
71#undef F
72#undef FR
73
74#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
75#define F(FunctionName) std::string FunctionName(const std::string& filepath);
76ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
77#undef F
78#undef FH
79 45
80} // namespace Common::FS::Android 46} // namespace Common::FS::Android
diff --git a/src/common/range_sets.h b/src/common/range_sets.h
new file mode 100644
index 000000000..f8fcee483
--- /dev/null
+++ b/src/common/range_sets.h
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Common {
11
12template <typename AddressType>
13class RangeSet {
14public:
15 RangeSet();
16 ~RangeSet();
17
18 RangeSet(RangeSet const&) = delete;
19 RangeSet& operator=(RangeSet const&) = delete;
20
21 RangeSet(RangeSet&& other);
22 RangeSet& operator=(RangeSet&& other);
23
24 void Add(AddressType base_address, size_t size);
25 void Subtract(AddressType base_address, size_t size);
26 void Clear();
27 bool Empty() const;
28
29 template <typename Func>
30 void ForEach(Func&& func) const;
31
32 template <typename Func>
33 void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
34
35private:
36 struct RangeSetImpl;
37 std::unique_ptr<RangeSetImpl> m_impl;
38};
39
40template <typename AddressType>
41class OverlapRangeSet {
42public:
43 OverlapRangeSet();
44 ~OverlapRangeSet();
45
46 OverlapRangeSet(OverlapRangeSet const&) = delete;
47 OverlapRangeSet& operator=(OverlapRangeSet const&) = delete;
48
49 OverlapRangeSet(OverlapRangeSet&& other);
50 OverlapRangeSet& operator=(OverlapRangeSet&& other);
51
52 void Add(AddressType base_address, size_t size);
53 void Subtract(AddressType base_address, size_t size);
54
55 template <typename Func>
56 void Subtract(AddressType base_address, size_t size, Func&& on_delete);
57
58 void DeleteAll(AddressType base_address, size_t size);
59 void Clear();
60 bool Empty() const;
61
62 template <typename Func>
63 void ForEach(Func&& func) const;
64
65 template <typename Func>
66 void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const;
67
68private:
69 struct OverlapRangeSetImpl;
70 std::unique_ptr<OverlapRangeSetImpl> m_impl;
71};
72
73} // namespace Common
diff --git a/src/common/range_sets.inc b/src/common/range_sets.inc
new file mode 100644
index 000000000..b83eceb7b
--- /dev/null
+++ b/src/common/range_sets.inc
@@ -0,0 +1,304 @@
1// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <limits>
7#include <utility>
8
9#include <boost/icl/interval.hpp>
10#include <boost/icl/interval_base_set.hpp>
11#include <boost/icl/interval_map.hpp>
12#include <boost/icl/interval_set.hpp>
13#include <boost/icl/split_interval_map.hpp>
14#include <boost/pool/pool.hpp>
15#include <boost/pool/pool_alloc.hpp>
16#include <boost/pool/poolfwd.hpp>
17
18#include "common/range_sets.h"
19
20namespace Common {
21
22namespace {
23template <class T>
24using RangeSetsAllocator =
25 boost::fast_pool_allocator<T, boost::default_user_allocator_new_delete,
26 boost::details::pool::default_mutex, 1024, 2048>;
27}
28
29template <typename AddressType>
30struct RangeSet<AddressType>::RangeSetImpl {
31 using IntervalSet = boost::icl::interval_set<
32 AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less),
33 RangeSetsAllocator>;
34 using IntervalType = typename IntervalSet::interval_type;
35
36 RangeSetImpl() = default;
37 ~RangeSetImpl() = default;
38
39 void Add(AddressType base_address, size_t size) {
40 AddressType end_address = base_address + static_cast<AddressType>(size);
41 IntervalType interval{base_address, end_address};
42 m_ranges_set.add(interval);
43 }
44
45 void Subtract(AddressType base_address, size_t size) {
46 AddressType end_address = base_address + static_cast<AddressType>(size);
47 IntervalType interval{base_address, end_address};
48 m_ranges_set.subtract(interval);
49 }
50
51 template <typename Func>
52 void ForEach(Func&& func) const {
53 if (m_ranges_set.empty()) {
54 return;
55 }
56 auto it = m_ranges_set.begin();
57 auto end_it = m_ranges_set.end();
58 for (; it != end_it; it++) {
59 const AddressType inter_addr_end = it->upper();
60 const AddressType inter_addr = it->lower();
61 func(inter_addr, inter_addr_end);
62 }
63 }
64
65 template <typename Func>
66 void ForEachInRange(AddressType base_addr, size_t size, Func&& func) const {
67 if (m_ranges_set.empty()) {
68 return;
69 }
70 const AddressType start_address = base_addr;
71 const AddressType end_address = start_address + size;
72 const RangeSetImpl::IntervalType search_interval{start_address, end_address};
73 auto it = m_ranges_set.lower_bound(search_interval);
74 if (it == m_ranges_set.end()) {
75 return;
76 }
77 auto end_it = m_ranges_set.upper_bound(search_interval);
78 for (; it != end_it; it++) {
79 AddressType inter_addr_end = it->upper();
80 AddressType inter_addr = it->lower();
81 if (inter_addr_end > end_address) {
82 inter_addr_end = end_address;
83 }
84 if (inter_addr < start_address) {
85 inter_addr = start_address;
86 }
87 func(inter_addr, inter_addr_end);
88 }
89 }
90
91 IntervalSet m_ranges_set;
92};
93
94template <typename AddressType>
95struct OverlapRangeSet<AddressType>::OverlapRangeSetImpl {
96 using IntervalSet = boost::icl::split_interval_map<
97 AddressType, s32, boost::icl::partial_enricher, std::less, boost::icl::inplace_plus,
98 boost::icl::inter_section,
99 ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), RangeSetsAllocator>;
100 using IntervalType = typename IntervalSet::interval_type;
101
102 OverlapRangeSetImpl() = default;
103 ~OverlapRangeSetImpl() = default;
104
105 void Add(AddressType base_address, size_t size) {
106 AddressType end_address = base_address + static_cast<AddressType>(size);
107 IntervalType interval{base_address, end_address};
108 m_split_ranges_set += std::make_pair(interval, 1);
109 }
110
111 template <bool has_on_delete, typename Func>
112 void Subtract(AddressType base_address, size_t size, s32 amount,
113 [[maybe_unused]] Func&& on_delete) {
114 if (m_split_ranges_set.empty()) {
115 return;
116 }
117 AddressType end_address = base_address + static_cast<AddressType>(size);
118 IntervalType interval{base_address, end_address};
119 bool any_removals = false;
120 m_split_ranges_set += std::make_pair(interval, -amount);
121 do {
122 any_removals = false;
123 auto it = m_split_ranges_set.lower_bound(interval);
124 if (it == m_split_ranges_set.end()) {
125 return;
126 }
127 auto end_it = m_split_ranges_set.upper_bound(interval);
128 for (; it != end_it; it++) {
129 if (it->second <= 0) {
130 if constexpr (has_on_delete) {
131 if (it->second == 0) {
132 on_delete(it->first.lower(), it->first.upper());
133 }
134 }
135 any_removals = true;
136 m_split_ranges_set.erase(it);
137 break;
138 }
139 }
140 } while (any_removals);
141 }
142
143 template <typename Func>
144 void ForEach(Func&& func) const {
145 if (m_split_ranges_set.empty()) {
146 return;
147 }
148 auto it = m_split_ranges_set.begin();
149 auto end_it = m_split_ranges_set.end();
150 for (; it != end_it; it++) {
151 const AddressType inter_addr_end = it->first.upper();
152 const AddressType inter_addr = it->first.lower();
153 func(inter_addr, inter_addr_end, it->second);
154 }
155 }
156
157 template <typename Func>
158 void ForEachInRange(AddressType base_address, size_t size, Func&& func) const {
159 if (m_split_ranges_set.empty()) {
160 return;
161 }
162 const AddressType start_address = base_address;
163 const AddressType end_address = start_address + size;
164 const OverlapRangeSetImpl::IntervalType search_interval{start_address, end_address};
165 auto it = m_split_ranges_set.lower_bound(search_interval);
166 if (it == m_split_ranges_set.end()) {
167 return;
168 }
169 auto end_it = m_split_ranges_set.upper_bound(search_interval);
170 for (; it != end_it; it++) {
171 auto& inter = it->first;
172 AddressType inter_addr_end = inter.upper();
173 AddressType inter_addr = inter.lower();
174 if (inter_addr_end > end_address) {
175 inter_addr_end = end_address;
176 }
177 if (inter_addr < start_address) {
178 inter_addr = start_address;
179 }
180 func(inter_addr, inter_addr_end, it->second);
181 }
182 }
183
184 IntervalSet m_split_ranges_set;
185};
186
187template <typename AddressType>
188RangeSet<AddressType>::RangeSet() {
189 m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
190}
191
192template <typename AddressType>
193RangeSet<AddressType>::~RangeSet() = default;
194
195template <typename AddressType>
196RangeSet<AddressType>::RangeSet(RangeSet&& other) {
197 m_impl = std::make_unique<RangeSet<AddressType>::RangeSetImpl>();
198 m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
199}
200
201template <typename AddressType>
202RangeSet<AddressType>& RangeSet<AddressType>::operator=(RangeSet&& other) {
203 m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set);
204}
205
206template <typename AddressType>
207void RangeSet<AddressType>::Add(AddressType base_address, size_t size) {
208 m_impl->Add(base_address, size);
209}
210
211template <typename AddressType>
212void RangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
213 m_impl->Subtract(base_address, size);
214}
215
216template <typename AddressType>
217void RangeSet<AddressType>::Clear() {
218 m_impl->m_ranges_set.clear();
219}
220
221template <typename AddressType>
222bool RangeSet<AddressType>::Empty() const {
223 return m_impl->m_ranges_set.empty();
224}
225
226template <typename AddressType>
227template <typename Func>
228void RangeSet<AddressType>::ForEach(Func&& func) const {
229 m_impl->ForEach(std::move(func));
230}
231
232template <typename AddressType>
233template <typename Func>
234void RangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
235 Func&& func) const {
236 m_impl->ForEachInRange(base_address, size, std::move(func));
237}
238
239template <typename AddressType>
240OverlapRangeSet<AddressType>::OverlapRangeSet() {
241 m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
242}
243
244template <typename AddressType>
245OverlapRangeSet<AddressType>::~OverlapRangeSet() = default;
246
247template <typename AddressType>
248OverlapRangeSet<AddressType>::OverlapRangeSet(OverlapRangeSet&& other) {
249 m_impl = std::make_unique<OverlapRangeSet<AddressType>::OverlapRangeSetImpl>();
250 m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
251}
252
253template <typename AddressType>
254OverlapRangeSet<AddressType>& OverlapRangeSet<AddressType>::operator=(OverlapRangeSet&& other) {
255 m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set);
256}
257
258template <typename AddressType>
259void OverlapRangeSet<AddressType>::Add(AddressType base_address, size_t size) {
260 m_impl->Add(base_address, size);
261}
262
263template <typename AddressType>
264void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size) {
265 m_impl->template Subtract<false>(base_address, size, 1, [](AddressType, AddressType) {});
266}
267
268template <typename AddressType>
269template <typename Func>
270void OverlapRangeSet<AddressType>::Subtract(AddressType base_address, size_t size,
271 Func&& on_delete) {
272 m_impl->template Subtract<true, Func>(base_address, size, 1, std::move(on_delete));
273}
274
275template <typename AddressType>
276void OverlapRangeSet<AddressType>::DeleteAll(AddressType base_address, size_t size) {
277 m_impl->template Subtract<false>(base_address, size, std::numeric_limits<s32>::max(),
278 [](AddressType, AddressType) {});
279}
280
281template <typename AddressType>
282void OverlapRangeSet<AddressType>::Clear() {
283 m_impl->m_split_ranges_set.clear();
284}
285
286template <typename AddressType>
287bool OverlapRangeSet<AddressType>::Empty() const {
288 return m_impl->m_split_ranges_set.empty();
289}
290
291template <typename AddressType>
292template <typename Func>
293void OverlapRangeSet<AddressType>::ForEach(Func&& func) const {
294 m_impl->ForEach(func);
295}
296
297template <typename AddressType>
298template <typename Func>
299void OverlapRangeSet<AddressType>::ForEachInRange(AddressType base_address, size_t size,
300 Func&& func) const {
301 m_impl->ForEachInRange(base_address, size, std::move(func));
302}
303
304} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 07709d4e5..80d388fe8 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -30,6 +30,7 @@ namespace Settings {
30#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED> 30#define SETTING(TYPE, RANGED) template class Setting<TYPE, RANGED>
31#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED> 31#define SWITCHABLE(TYPE, RANGED) template class SwitchableSetting<TYPE, RANGED>
32 32
33SETTING(AppletMode, false);
33SETTING(AudioEngine, false); 34SETTING(AudioEngine, false);
34SETTING(bool, false); 35SETTING(bool, false);
35SETTING(int, false); 36SETTING(int, false);
@@ -215,6 +216,8 @@ const char* TranslateCategory(Category category) {
215 return "Debugging"; 216 return "Debugging";
216 case Category::GpuDriver: 217 case Category::GpuDriver:
217 return "GpuDriver"; 218 return "GpuDriver";
219 case Category::LibraryApplet:
220 return "LibraryApplet";
218 case Category::Miscellaneous: 221 case Category::Miscellaneous:
219 return "Miscellaneous"; 222 return "Miscellaneous";
220 case Category::Network: 223 case Category::Network:
diff --git a/src/common/settings.h b/src/common/settings.h
index f1b1add56..aa054dc24 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -133,6 +133,38 @@ struct TouchFromButtonMap {
133struct Values { 133struct Values {
134 Linkage linkage{}; 134 Linkage linkage{};
135 135
136 // Applet
137 Setting<AppletMode> cabinet_applet_mode{linkage, AppletMode::LLE, "cabinet_applet_mode",
138 Category::LibraryApplet};
139 Setting<AppletMode> controller_applet_mode{linkage, AppletMode::HLE, "controller_applet_mode",
140 Category::LibraryApplet};
141 Setting<AppletMode> data_erase_applet_mode{linkage, AppletMode::HLE, "data_erase_applet_mode",
142 Category::LibraryApplet};
143 Setting<AppletMode> error_applet_mode{linkage, AppletMode::HLE, "error_applet_mode",
144 Category::LibraryApplet};
145 Setting<AppletMode> net_connect_applet_mode{linkage, AppletMode::HLE, "net_connect_applet_mode",
146 Category::LibraryApplet};
147 Setting<AppletMode> player_select_applet_mode{
148 linkage, AppletMode::HLE, "player_select_applet_mode", Category::LibraryApplet};
149 Setting<AppletMode> swkbd_applet_mode{linkage, AppletMode::LLE, "swkbd_applet_mode",
150 Category::LibraryApplet};
151 Setting<AppletMode> mii_edit_applet_mode{linkage, AppletMode::LLE, "mii_edit_applet_mode",
152 Category::LibraryApplet};
153 Setting<AppletMode> web_applet_mode{linkage, AppletMode::HLE, "web_applet_mode",
154 Category::LibraryApplet};
155 Setting<AppletMode> shop_applet_mode{linkage, AppletMode::HLE, "shop_applet_mode",
156 Category::LibraryApplet};
157 Setting<AppletMode> photo_viewer_applet_mode{
158 linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet};
159 Setting<AppletMode> offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode",
160 Category::LibraryApplet};
161 Setting<AppletMode> login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode",
162 Category::LibraryApplet};
163 Setting<AppletMode> wifi_web_auth_applet_mode{
164 linkage, AppletMode::HLE, "wifi_web_auth_applet_mode", Category::LibraryApplet};
165 Setting<AppletMode> my_page_applet_mode{linkage, AppletMode::LLE, "my_page_applet_mode",
166 Category::LibraryApplet};
167
136 // Audio 168 // Audio
137 SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine", 169 SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
138 Category::Audio, Specialization::RuntimeList}; 170 Category::Audio, Specialization::RuntimeList};
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 987489e8a..2df3f0809 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -44,6 +44,7 @@ enum class Category : u32 {
44 Services, 44 Services,
45 Paths, 45 Paths,
46 Linux, 46 Linux,
47 LibraryApplet,
47 MaxEnum, 48 MaxEnum,
48}; 49};
49 50
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index 617036588..f42367e67 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -151,6 +151,8 @@ ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
151 151
152ENUM(ConsoleMode, Handheld, Docked); 152ENUM(ConsoleMode, Handheld, Docked);
153 153
154ENUM(AppletMode, HLE, LLE);
155
154template <typename Type> 156template <typename Type>
155inline std::string CanonicalizeEnum(Type id) { 157inline std::string CanonicalizeEnum(Type id) {
156 const auto group = EnumMetadata<Type>::Canonicalizations(); 158 const auto group = EnumMetadata<Type>::Canonicalizations();
diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h
new file mode 100644
index 000000000..34ff7de94
--- /dev/null
+++ b/src/common/slot_vector.h
@@ -0,0 +1,227 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <bit>
8#include <numeric>
9#include <type_traits>
10#include <utility>
11#include <vector>
12
13#include "common/assert.h"
14#include "common/common_types.h"
15#include "common/polyfill_ranges.h"
16
17namespace Common {
18
19struct SlotId {
20 static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
21
22 constexpr auto operator<=>(const SlotId&) const noexcept = default;
23
24 constexpr explicit operator bool() const noexcept {
25 return index != INVALID_INDEX;
26 }
27
28 u32 index = INVALID_INDEX;
29};
30
31template <class T>
32 requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
33class SlotVector {
34public:
35 class Iterator {
36 friend SlotVector<T>;
37
38 public:
39 constexpr Iterator() = default;
40
41 Iterator& operator++() noexcept {
42 const u64* const bitset = slot_vector->stored_bitset.data();
43 const u32 size = static_cast<u32>(slot_vector->stored_bitset.size()) * 64;
44 if (id.index < size) {
45 do {
46 ++id.index;
47 } while (id.index < size && !IsValid(bitset));
48 if (id.index == size) {
49 id.index = SlotId::INVALID_INDEX;
50 }
51 }
52 return *this;
53 }
54
55 Iterator operator++(int) noexcept {
56 const Iterator copy{*this};
57 ++*this;
58 return copy;
59 }
60
61 bool operator==(const Iterator& other) const noexcept {
62 return id.index == other.id.index;
63 }
64
65 bool operator!=(const Iterator& other) const noexcept {
66 return id.index != other.id.index;
67 }
68
69 std::pair<SlotId, T*> operator*() const noexcept {
70 return {id, std::addressof((*slot_vector)[id])};
71 }
72
73 T* operator->() const noexcept {
74 return std::addressof((*slot_vector)[id]);
75 }
76
77 private:
78 Iterator(SlotVector<T>* slot_vector_, SlotId id_) noexcept
79 : slot_vector{slot_vector_}, id{id_} {}
80
81 bool IsValid(const u64* bitset) const noexcept {
82 return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0;
83 }
84
85 SlotVector<T>* slot_vector;
86 SlotId id;
87 };
88
89 ~SlotVector() noexcept {
90 size_t index = 0;
91 for (u64 bits : stored_bitset) {
92 for (size_t bit = 0; bits; ++bit, bits >>= 1) {
93 if ((bits & 1) != 0) {
94 values[index + bit].object.~T();
95 }
96 }
97 index += 64;
98 }
99 delete[] values;
100 }
101
102 [[nodiscard]] T& operator[](SlotId id) noexcept {
103 ValidateIndex(id);
104 return values[id.index].object;
105 }
106
107 [[nodiscard]] const T& operator[](SlotId id) const noexcept {
108 ValidateIndex(id);
109 return values[id.index].object;
110 }
111
112 template <typename... Args>
113 [[nodiscard]] SlotId insert(Args&&... args) noexcept {
114 const u32 index = FreeValueIndex();
115 new (&values[index].object) T(std::forward<Args>(args)...);
116 SetStorageBit(index);
117
118 return SlotId{index};
119 }
120
121 void erase(SlotId id) noexcept {
122 values[id.index].object.~T();
123 free_list.push_back(id.index);
124 ResetStorageBit(id.index);
125 }
126
127 [[nodiscard]] Iterator begin() noexcept {
128 const auto it = std::ranges::find_if(stored_bitset, [](u64 value) { return value != 0; });
129 if (it == stored_bitset.end()) {
130 return end();
131 }
132 const u32 word_index = static_cast<u32>(std::distance(it, stored_bitset.begin()));
133 const SlotId first_id{word_index * 64 + static_cast<u32>(std::countr_zero(*it))};
134 return Iterator(this, first_id);
135 }
136
137 [[nodiscard]] Iterator end() noexcept {
138 return Iterator(this, SlotId{SlotId::INVALID_INDEX});
139 }
140
141 [[nodiscard]] size_t size() const noexcept {
142 return values_capacity - free_list.size();
143 }
144
145private:
146 struct NonTrivialDummy {
147 NonTrivialDummy() noexcept {}
148 };
149
150 union Entry {
151 Entry() noexcept : dummy{} {}
152 ~Entry() noexcept {}
153
154 NonTrivialDummy dummy;
155 T object;
156 };
157
158 void SetStorageBit(u32 index) noexcept {
159 stored_bitset[index / 64] |= u64(1) << (index % 64);
160 }
161
162 void ResetStorageBit(u32 index) noexcept {
163 stored_bitset[index / 64] &= ~(u64(1) << (index % 64));
164 }
165
166 bool ReadStorageBit(u32 index) noexcept {
167 return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0;
168 }
169
170 void ValidateIndex(SlotId id) const noexcept {
171 DEBUG_ASSERT(id);
172 DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
173 DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);
174 }
175
176 [[nodiscard]] u32 FreeValueIndex() noexcept {
177 if (free_list.empty()) {
178 Reserve(values_capacity ? (values_capacity << 1) : 1);
179 }
180 const u32 free_index = free_list.back();
181 free_list.pop_back();
182 return free_index;
183 }
184
185 void Reserve(size_t new_capacity) noexcept {
186 Entry* const new_values = new Entry[new_capacity];
187 size_t index = 0;
188 for (u64 bits : stored_bitset) {
189 for (size_t bit = 0; bits; ++bit, bits >>= 1) {
190 const size_t i = index + bit;
191 if ((bits & 1) == 0) {
192 continue;
193 }
194 T& old_value = values[i].object;
195 new (&new_values[i].object) T(std::move(old_value));
196 old_value.~T();
197 }
198 index += 64;
199 }
200
201 stored_bitset.resize((new_capacity + 63) / 64);
202
203 const size_t old_free_size = free_list.size();
204 free_list.resize(old_free_size + (new_capacity - values_capacity));
205 std::iota(free_list.begin() + old_free_size, free_list.end(),
206 static_cast<u32>(values_capacity));
207
208 delete[] values;
209 values = new_values;
210 values_capacity = new_capacity;
211 }
212
213 Entry* values = nullptr;
214 size_t values_capacity = 0;
215
216 std::vector<u64> stored_bitset;
217 std::vector<u32> free_list;
218};
219
220} // namespace Common
221
222template <>
223struct std::hash<Common::SlotId> {
224 size_t operator()(const Common::SlotId& id) const noexcept {
225 return std::hash<u32>{}(id.index);
226 }
227};