summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt31
-rw-r--r--src/android/app/src/main/AndroidManifest.xml9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt21
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt45
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt52
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt31
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt103
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt (renamed from src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt)6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt20
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt60
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt154
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt316
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt36
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt27
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt247
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt55
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt46
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt53
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt213
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt90
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt285
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt151
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt538
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt58
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt8
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt255
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt159
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt62
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt235
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt184
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt59
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt96
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt206
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt25
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt16
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt77
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt33
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt48
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt2
-rw-r--r--src/android/app/src/main/jni/config.cpp36
-rw-r--r--src/android/app/src/main/jni/config.h24
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp14
-rw-r--r--src/android/app/src/main/jni/id_cache.h2
-rw-r--r--src/android/app/src/main/jni/native.cpp53
-rw-r--r--src/android/app/src/main/jni/native_config.cpp237
-rw-r--r--src/android/app/src/main/jni/uisettings.cpp10
-rw-r--r--src/android/app/src/main/jni/uisettings.h29
-rw-r--r--src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml16
-rw-r--r--src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml16
-rw-r--r--src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml16
-rw-r--r--src/android/app/src/main/res/anim/anim_settings_fragment_in.xml16
-rw-r--r--src/android/app/src/main/res/anim/anim_settings_fragment_out.xml10
-rw-r--r--src/android/app/src/main/res/animator/menu_slide_in_from_start.xml20
-rw-r--r--src/android/app/src/main/res/animator/menu_slide_out_to_start.xml21
-rw-r--r--src/android/app/src/main/res/layout/activity_settings.xml52
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml83
-rw-r--r--src/android/app/src/main/res/layout/fragment_settings.xml39
-rw-r--r--src/android/app/src/main/res/layout/fragment_settings_search.xml120
-rw-r--r--src/android/app/src/main/res/menu/menu_settings.xml11
-rw-r--r--src/android/app/src/main/res/navigation/emulation_navigation.xml21
-rw-r--r--src/android/app/src/main/res/navigation/home_navigation.xml21
-rw-r--r--src/android/app/src/main/res/navigation/settings_navigation.xml32
-rw-r--r--src/android/app/src/main/res/values-de/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-es/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-fr/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-it/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-ja/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-ko/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-nb/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-pl/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-pt-rBR/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-pt-rPT/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-ru/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-uk/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-zh-rCN/strings.xml1
-rw-r--r--src/android/app/src/main/res/values-zh-rTW/strings.xml1
-rw-r--r--src/android/app/src/main/res/values/arrays.xml10
-rw-r--r--src/android/app/src/main/res/values/strings.xml5
-rw-r--r--src/audio_core/CMakeLists.txt15
-rw-r--r--src/audio_core/adsp/adsp.cpp18
-rw-r--r--src/audio_core/adsp/adsp.h50
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp220
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/audio_renderer.h116
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/command_buffer.h23
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp (renamed from src/audio_core/renderer/adsp/command_list_processor.cpp)29
-rw-r--r--src/audio_core/adsp/apps/audio_renderer/command_list_processor.h (renamed from src/audio_core/renderer/adsp/command_list_processor.h)21
-rw-r--r--src/audio_core/adsp/mailbox.h69
-rw-r--r--src/audio_core/audio_core.cpp4
-rw-r--r--src/audio_core/audio_core.h6
-rw-r--r--src/audio_core/audio_event.cpp1
-rw-r--r--src/audio_core/audio_in_manager.cpp2
-rw-r--r--src/audio_core/audio_in_manager.h4
-rw-r--r--src/audio_core/audio_out_manager.cpp2
-rw-r--r--src/audio_core/audio_out_manager.h3
-rw-r--r--src/audio_core/audio_render_manager.cpp4
-rw-r--r--src/audio_core/audio_render_manager.h4
-rw-r--r--src/audio_core/common/audio_renderer_parameter.h6
-rw-r--r--src/audio_core/renderer/adsp/adsp.cpp117
-rw-r--r--src/audio_core/renderer/adsp/adsp.h171
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.cpp225
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.h204
-rw-r--r--src/audio_core/renderer/adsp/command_buffer.h21
-rw-r--r--src/audio_core/renderer/audio_device.cpp4
-rw-r--r--src/audio_core/renderer/audio_device.h4
-rw-r--r--src/audio_core/renderer/audio_renderer.cpp4
-rw-r--r--src/audio_core/renderer/audio_renderer.h6
-rw-r--r--src/audio_core/renderer/behavior/behavior_info.cpp4
-rw-r--r--src/audio_core/renderer/behavior/behavior_info.h8
-rw-r--r--src/audio_core/renderer/behavior/info_updater.cpp4
-rw-r--r--src/audio_core/renderer/behavior/info_updater.h4
-rw-r--r--src/audio_core/renderer/command/command_buffer.cpp4
-rw-r--r--src/audio_core/renderer/command/command_buffer.h4
-rw-r--r--src/audio_core/renderer/command/command_generator.cpp4
-rw-r--r--src/audio_core/renderer/command/command_generator.h4
-rw-r--r--src/audio_core/renderer/command/command_list_header.h4
-rw-r--r--src/audio_core/renderer/command/command_processing_time_estimator.cpp4
-rw-r--r--src/audio_core/renderer/command/command_processing_time_estimator.h4
-rw-r--r--src/audio_core/renderer/command/data_source/adpcm.cpp24
-rw-r--r--src/audio_core/renderer/command/data_source/adpcm.h23
-rw-r--r--src/audio_core/renderer/command/data_source/decode.cpp110
-rw-r--r--src/audio_core/renderer/command/data_source/decode.h4
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_float.cpp28
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_float.h23
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_int16.cpp28
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_int16.h23
-rw-r--r--src/audio_core/renderer/command/effect/aux_.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/aux_.h13
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.cpp14
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.h13
-rw-r--r--src/audio_core/renderer/command/effect/capture.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/capture.h13
-rw-r--r--src/audio_core/renderer/command/effect/compressor.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/compressor.h13
-rw-r--r--src/audio_core/renderer/command/effect/delay.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/delay.h13
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.h13
-rw-r--r--src/audio_core/renderer/command/effect/light_limiter.cpp22
-rw-r--r--src/audio_core/renderer/command/effect/light_limiter.h19
-rw-r--r--src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp14
-rw-r--r--src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h13
-rw-r--r--src/audio_core/renderer/command/effect/reverb.cpp12
-rw-r--r--src/audio_core/renderer/command/effect/reverb.h13
-rw-r--r--src/audio_core/renderer/command/icommand.h17
-rw-r--r--src/audio_core/renderer/command/mix/clear_mix.cpp14
-rw-r--r--src/audio_core/renderer/command/mix/clear_mix.h13
-rw-r--r--src/audio_core/renderer/command/mix/copy_mix.cpp14
-rw-r--r--src/audio_core/renderer/command/mix/copy_mix.h13
-rw-r--r--src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp14
-rw-r--r--src/audio_core/renderer/command/mix/depop_for_mix_buffers.h13
-rw-r--r--src/audio_core/renderer/command/mix/depop_prepare.cpp14
-rw-r--r--src/audio_core/renderer/command/mix/depop_prepare.h13
-rw-r--r--src/audio_core/renderer/command/mix/mix.cpp12
-rw-r--r--src/audio_core/renderer/command/mix/mix.h13
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp.cpp13
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp.h13
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp13
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp_grouped.h13
-rw-r--r--src/audio_core/renderer/command/mix/volume.cpp12
-rw-r--r--src/audio_core/renderer/command/mix/volume.h13
-rw-r--r--src/audio_core/renderer/command/mix/volume_ramp.cpp13
-rw-r--r--src/audio_core/renderer/command/mix/volume_ramp.h13
-rw-r--r--src/audio_core/renderer/command/performance/performance.cpp20
-rw-r--r--src/audio_core/renderer/command/performance/performance.h13
-rw-r--r--src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp14
-rw-r--r--src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h13
-rw-r--r--src/audio_core/renderer/command/resample/resample.cpp4
-rw-r--r--src/audio_core/renderer/command/resample/resample.h4
-rw-r--r--src/audio_core/renderer/command/resample/upsample.cpp12
-rw-r--r--src/audio_core/renderer/command/resample/upsample.h13
-rw-r--r--src/audio_core/renderer/command/sink/circular_buffer.cpp14
-rw-r--r--src/audio_core/renderer/command/sink/circular_buffer.h13
-rw-r--r--src/audio_core/renderer/command/sink/device.cpp12
-rw-r--r--src/audio_core/renderer/command/sink/device.h13
-rw-r--r--src/audio_core/renderer/effect/aux_.cpp4
-rw-r--r--src/audio_core/renderer/effect/aux_.h4
-rw-r--r--src/audio_core/renderer/effect/biquad_filter.cpp4
-rw-r--r--src/audio_core/renderer/effect/biquad_filter.h4
-rw-r--r--src/audio_core/renderer/effect/buffer_mixer.cpp4
-rw-r--r--src/audio_core/renderer/effect/buffer_mixer.h4
-rw-r--r--src/audio_core/renderer/effect/capture.cpp4
-rw-r--r--src/audio_core/renderer/effect/capture.h4
-rw-r--r--src/audio_core/renderer/effect/compressor.cpp4
-rw-r--r--src/audio_core/renderer/effect/compressor.h4
-rw-r--r--src/audio_core/renderer/effect/delay.cpp4
-rw-r--r--src/audio_core/renderer/effect/delay.h4
-rw-r--r--src/audio_core/renderer/effect/effect_context.cpp4
-rw-r--r--src/audio_core/renderer/effect/effect_context.h4
-rw-r--r--src/audio_core/renderer/effect/effect_info_base.h4
-rw-r--r--src/audio_core/renderer/effect/effect_reset.h4
-rw-r--r--src/audio_core/renderer/effect/effect_result_state.h4
-rw-r--r--src/audio_core/renderer/effect/i3dl2.cpp4
-rw-r--r--src/audio_core/renderer/effect/i3dl2.h4
-rw-r--r--src/audio_core/renderer/effect/light_limiter.cpp4
-rw-r--r--src/audio_core/renderer/effect/light_limiter.h4
-rw-r--r--src/audio_core/renderer/effect/reverb.cpp4
-rw-r--r--src/audio_core/renderer/effect/reverb.h4
-rw-r--r--src/audio_core/renderer/memory/address_info.h4
-rw-r--r--src/audio_core/renderer/memory/memory_pool_info.cpp4
-rw-r--r--src/audio_core/renderer/memory/memory_pool_info.h4
-rw-r--r--src/audio_core/renderer/memory/pool_mapper.cpp4
-rw-r--r--src/audio_core/renderer/memory/pool_mapper.h4
-rw-r--r--src/audio_core/renderer/mix/mix_context.cpp4
-rw-r--r--src/audio_core/renderer/mix/mix_context.h4
-rw-r--r--src/audio_core/renderer/mix/mix_info.cpp4
-rw-r--r--src/audio_core/renderer/mix/mix_info.h4
-rw-r--r--src/audio_core/renderer/nodes/bit_array.h4
-rw-r--r--src/audio_core/renderer/nodes/edge_matrix.cpp4
-rw-r--r--src/audio_core/renderer/nodes/edge_matrix.h4
-rw-r--r--src/audio_core/renderer/nodes/node_states.cpp4
-rw-r--r--src/audio_core/renderer/nodes/node_states.h4
-rw-r--r--src/audio_core/renderer/performance/detail_aspect.cpp4
-rw-r--r--src/audio_core/renderer/performance/detail_aspect.h4
-rw-r--r--src/audio_core/renderer/performance/entry_aspect.cpp4
-rw-r--r--src/audio_core/renderer/performance/entry_aspect.h4
-rw-r--r--src/audio_core/renderer/performance/performance_detail.h4
-rw-r--r--src/audio_core/renderer/performance/performance_entry.h4
-rw-r--r--src/audio_core/renderer/performance/performance_entry_addresses.h4
-rw-r--r--src/audio_core/renderer/performance/performance_frame_header.h4
-rw-r--r--src/audio_core/renderer/performance/performance_manager.cpp4
-rw-r--r--src/audio_core/renderer/performance/performance_manager.h4
-rw-r--r--src/audio_core/renderer/sink/circular_buffer_sink_info.cpp4
-rw-r--r--src/audio_core/renderer/sink/circular_buffer_sink_info.h4
-rw-r--r--src/audio_core/renderer/sink/device_sink_info.cpp4
-rw-r--r--src/audio_core/renderer/sink/device_sink_info.h4
-rw-r--r--src/audio_core/renderer/sink/sink_context.cpp4
-rw-r--r--src/audio_core/renderer/sink/sink_context.h4
-rw-r--r--src/audio_core/renderer/sink/sink_info_base.cpp4
-rw-r--r--src/audio_core/renderer/sink/sink_info_base.h4
-rw-r--r--src/audio_core/renderer/splitter/splitter_context.cpp4
-rw-r--r--src/audio_core/renderer/splitter/splitter_context.h4
-rw-r--r--src/audio_core/renderer/splitter/splitter_destinations_data.cpp4
-rw-r--r--src/audio_core/renderer/splitter/splitter_destinations_data.h4
-rw-r--r--src/audio_core/renderer/splitter/splitter_info.cpp4
-rw-r--r--src/audio_core/renderer/splitter/splitter_info.h4
-rw-r--r--src/audio_core/renderer/system.cpp44
-rw-r--r--src/audio_core/renderer/system.h16
-rw-r--r--src/audio_core/renderer/system_manager.cpp30
-rw-r--r--src/audio_core/renderer/system_manager.h21
-rw-r--r--src/audio_core/renderer/upsampler/upsampler_info.h4
-rw-r--r--src/audio_core/renderer/upsampler/upsampler_manager.cpp4
-rw-r--r--src/audio_core/renderer/upsampler/upsampler_manager.h4
-rw-r--r--src/audio_core/renderer/upsampler/upsampler_state.h4
-rw-r--r--src/audio_core/renderer/voice/voice_channel_resource.h4
-rw-r--r--src/audio_core/renderer/voice/voice_context.cpp4
-rw-r--r--src/audio_core/renderer/voice/voice_context.h4
-rw-r--r--src/audio_core/renderer/voice/voice_info.cpp4
-rw-r--r--src/audio_core/renderer/voice/voice_info.h4
-rw-r--r--src/audio_core/renderer/voice/voice_state.h4
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp47
-rw-r--r--src/audio_core/sink/cubeb_sink.h7
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp40
-rw-r--r--src/audio_core/sink/sdl2_sink.h7
-rw-r--r--src/audio_core/sink/sink_details.cpp45
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/fs/path_util.cpp6
-rw-r--r--src/common/settings.cpp7
-rw-r--r--src/common/settings.h10
-rw-r--r--src/common/settings_common.cpp1
-rw-r--r--src/common/settings_common.h3
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/common/swap.h5
-rw-r--r--src/core/CMakeLists.txt16
-rw-r--r--src/core/core.cpp31
-rw-r--r--src/core/core.h14
-rw-r--r--src/core/crypto/key_manager.cpp226
-rw-r--r--src/core/crypto/key_manager.h59
-rw-r--r--src/core/file_sys/content_archive.cpp17
-rw-r--r--src/core/file_sys/nca_metadata.cpp4
-rw-r--r--src/core/file_sys/nca_metadata.h1
-rw-r--r--src/core/file_sys/registered_cache.cpp28
-rw-r--r--src/core/file_sys/registered_cache.h5
-rw-r--r--src/core/file_sys/submission_package.cpp35
-rw-r--r--src/core/file_sys/submission_package.h1
-rw-r--r--src/core/frontend/applets/controller.cpp4
-rw-r--r--src/core/frontend/framebuffer_layout.cpp3
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp1
-rw-r--r--src/core/hle/service/am/am.cpp70
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp11
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit_types.h3
-rw-r--r--src/core/hle/service/apm/apm_controller.cpp4
-rw-r--r--src/core/hle/service/audio/audin_u.cpp4
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.h2
-rw-r--r--src/core/hle/service/audio/hwopus.cpp55
-rw-r--r--src/core/hle/service/audio/hwopus.h6
-rw-r--r--src/core/hle/service/es/es.cpp10
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp2
-rw-r--r--src/core/hle/service/mii/mii.cpp139
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp717
-rw-r--r--src/core/hle/service/mii/mii_manager.h42
-rw-r--r--src/core/hle/service/mii/mii_result.h20
-rw-r--r--src/core/hle/service/mii/mii_types.h694
-rw-r--r--src/core/hle/service/mii/mii_util.h59
-rw-r--r--src/core/hle/service/mii/raw_data.h26
-rw-r--r--src/core/hle/service/mii/types.h553
-rw-r--r--src/core/hle/service/mii/types/char_info.cpp482
-rw-r--r--src/core/hle/service/mii/types/char_info.h137
-rw-r--r--src/core/hle/service/mii/types/core_data.cpp601
-rw-r--r--src/core/hle/service/mii/types/core_data.h216
-rw-r--r--src/core/hle/service/mii/types/raw_data.cpp (renamed from src/core/hle/service/mii/raw_data.cpp)1400
-rw-r--r--src/core/hle/service/mii/types/raw_data.h73
-rw-r--r--src/core/hle/service/mii/types/store_data.cpp643
-rw-r--r--src/core/hle/service/mii/types/store_data.h145
-rw-r--r--src/core/hle/service/mii/types/ver3_store_data.cpp241
-rw-r--r--src/core/hle/service/mii/types/ver3_store_data.h160
-rw-r--r--src/core/hle/service/nfc/common/device.cpp25
-rw-r--r--src/core/hle/service/nfp/nfp_types.h6
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp4
-rw-r--r--src/core/hle/service/sockets/nsd.cpp11
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp12
-rw-r--r--src/core/hle/service/ssl/ssl_backend_schannel.cpp3
-rw-r--r--src/core/hle/service/vi/vi.cpp2
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/loader/loader.h10
-rw-r--r--src/core/loader/nca.cpp76
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nsp.cpp36
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp34
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/telemetry_session.cpp3
-rw-r--r--src/dedicated_room/yuzu_room.cpp6
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/input_poller.cpp10
-rw-r--r--src/network/room.cpp2
-rw-r--r--src/shader_recompiler/CMakeLists.txt2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp35
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp7
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp3
-rw-r--r--src/tests/common/ring_buffer.cpp2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h10
-rw-r--r--src/video_core/dma_pusher.cpp34
-rw-r--r--src/video_core/dma_pusher.h5
-rw-r--r--src/video_core/engines/engine_interface.h8
-rw-r--r--src/video_core/engines/engine_upload.h8
-rw-r--r--src/video_core/engines/kepler_compute.cpp20
-rw-r--r--src/video_core/engines/kepler_compute.h17
-rw-r--r--src/video_core/engines/maxwell_3d.cpp6
-rw-r--r--src/video_core/engines/puller.cpp15
-rw-r--r--src/video_core/host1x/codecs/codec.cpp3
-rw-r--r--src/video_core/macro/macro.cpp24
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp11
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp18
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.cpp27
-rw-r--r--src/video_core/vulkan_common/vulkan_debug_callback.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.h5
-rw-r--r--src/web_service/verify_user_jwt.cpp1
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.cpp3
-rw-r--r--src/yuzu/applets/qt_controller.cpp14
-rw-r--r--src/yuzu/bootmanager.cpp4
-rw-r--r--src/yuzu/configuration/config.cpp9
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp28
-rw-r--r--src/yuzu/configuration/configure_input.cpp14
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp7
-rw-r--r--src/yuzu/configuration/configure_system.cpp5
-rw-r--r--src/yuzu/configuration/shared_translation.cpp5
-rw-r--r--src/yuzu/configuration/shared_widget.cpp68
-rw-r--r--src/yuzu/configuration/shared_widget.h6
-rw-r--r--src/yuzu/game_list.cpp9
-rw-r--r--src/yuzu/game_list.h4
-rw-r--r--src/yuzu/main.cpp406
-rw-r--r--src/yuzu/main.h16
-rw-r--r--src/yuzu/main.ui6
-rw-r--r--src/yuzu_cmd/yuzu.cpp3
403 files changed, 10004 insertions, 6469 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7bb88c8ea..d7f68618c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -24,7 +24,7 @@ if (MSVC)
24 # Ensure that projects build with Unicode support. 24 # Ensure that projects build with Unicode support.
25 add_definitions(-DUNICODE -D_UNICODE) 25 add_definitions(-DUNICODE -D_UNICODE)
26 26
27 # /W3 - Level 3 warnings 27 # /W4 - Level 4 warnings
28 # /MP - Multi-threaded compilation 28 # /MP - Multi-threaded compilation
29 # /Zi - Output debugging information 29 # /Zi - Output debugging information
30 # /Zm - Specifies the precompiled header memory allocation limit 30 # /Zm - Specifies the precompiled header memory allocation limit
@@ -61,7 +61,7 @@ if (MSVC)
61 /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers 61 /external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers
62 62
63 # Warnings 63 # Warnings
64 /W3 64 /W4
65 /WX 65 /WX
66 66
67 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled 67 /we4062 # Enumerator 'identifier' in a switch of enum 'enumeration' is not handled
@@ -84,12 +84,17 @@ if (MSVC)
84 84
85 /wd4100 # 'identifier': unreferenced formal parameter 85 /wd4100 # 'identifier': unreferenced formal parameter
86 /wd4324 # 'struct_name': structure was padded due to __declspec(align()) 86 /wd4324 # 'struct_name': structure was padded due to __declspec(align())
87 /wd4201 # nonstandard extension used : nameless struct/union
88 /wd4702 # unreachable code (when used with LTO)
87 ) 89 )
88 90
89 if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS) 91 if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS)
90 # when caching, we need to use /Z7 to downgrade debug info to use an older but more cacheable format 92 # when caching, we need to use /Z7 to downgrade debug info to use an older but more cacheable format
91 # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21 93 # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21
92 add_compile_options(/Z7) 94 add_compile_options(/Z7)
95 # Avoid D9025 warning
96 string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
97 string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
93 else() 98 else()
94 add_compile_options(/Zi) 99 add_compile_options(/Zi)
95 endif() 100 endif()
@@ -105,6 +110,8 @@ if (MSVC)
105 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) 110 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
106else() 111else()
107 add_compile_options( 112 add_compile_options(
113 -fwrapv
114
108 -Werror=all 115 -Werror=all
109 -Werror=extra 116 -Werror=extra
110 -Werror=missing-declarations 117 -Werror=missing-declarations
@@ -114,19 +121,21 @@ else()
114 -Wno-attributes 121 -Wno-attributes
115 -Wno-invalid-offsetof 122 -Wno-invalid-offsetof
116 -Wno-unused-parameter 123 -Wno-unused-parameter
117
118 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
119 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
120 $<$<CXX_COMPILER_ID:Clang>:-Werror=shadow-uncaptured-local>
121 $<$<CXX_COMPILER_ID:Clang>:-Werror=implicit-fallthrough>
122 $<$<CXX_COMPILER_ID:Clang>:-Werror=type-limits>
123 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
124 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
125 ) 124 )
126 125
126 if (CMAKE_CXX_COMPILER_ID MATCHES Clang) # Clang or AppleClang
127 add_compile_options(
128 -Wno-braced-scalar-init
129 -Wno-unused-private-field
130 -Wno-nullability-completeness
131 -Werror=shadow-uncaptured-local
132 -Werror=implicit-fallthrough
133 -Werror=type-limits
134 )
135 endif()
136
127 if (ARCHITECTURE_x86_64) 137 if (ARCHITECTURE_x86_64)
128 add_compile_options("-mcx16") 138 add_compile_options("-mcx16")
129 add_compile_options("-fwrapv")
130 endif() 139 endif()
131 140
132 if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) 141 if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 36e2dac98..832c08e15 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -56,7 +56,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
56 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" 56 android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
57 android:theme="@style/Theme.Yuzu.Main" 57 android:theme="@style/Theme.Yuzu.Main"
58 android:launchMode="singleTop" 58 android:launchMode="singleTop"
59 android:screenOrientation="userLandscape"
60 android:supportsPictureInPicture="true" 59 android:supportsPictureInPicture="true"
61 android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" 60 android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
62 android:exported="true"> 61 android:exported="true">
@@ -67,6 +66,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
67 <data android:mimeType="application/octet-stream" /> 66 <data android:mimeType="application/octet-stream" />
68 </intent-filter> 67 </intent-filter>
69 68
69 <intent-filter>
70 <action android:name="android.intent.action.VIEW" />
71 <category android:name="android.intent.category.DEFAULT" />
72 <data
73 android:mimeType="application/octet-stream"
74 android:scheme="content"/>
75 </intent-filter>
76
70 <meta-data 77 <meta-data
71 android:name="android.nfc.action.TECH_DISCOVERED" 78 android:name="android.nfc.action.TECH_DISCOVERED"
72 android:resource="@xml/nfc_tech_filter" /> 79 android:resource="@xml/nfc_tech_filter" />
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index 9c32e044c..c8706d7a6 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -22,9 +22,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil.exists
22import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize 22import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
23import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory 23import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
24import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri 24import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
25import org.yuzu.yuzu_emu.utils.Log.error 25import org.yuzu.yuzu_emu.utils.Log
26import org.yuzu.yuzu_emu.utils.Log.verbose
27import org.yuzu.yuzu_emu.utils.Log.warning
28import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable 26import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
29 27
30/** 28/**
@@ -219,10 +217,6 @@ object NativeLibrary {
219 217
220 external fun reloadSettings() 218 external fun reloadSettings()
221 219
222 external fun getUserSetting(gameID: String?, Section: String?, Key: String?): String?
223
224 external fun setUserSetting(gameID: String?, Section: String?, Key: String?, Value: String?)
225
226 external fun initGameIni(gameID: String?) 220 external fun initGameIni(gameID: String?)
227 221
228 /** 222 /**
@@ -413,14 +407,17 @@ object NativeLibrary {
413 details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) } 407 details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) }
414 ) 408 )
415 } 409 }
410
416 CoreError.ErrorSavestate -> { 411 CoreError.ErrorSavestate -> {
417 title = emulationActivity.getString(R.string.save_load_error) 412 title = emulationActivity.getString(R.string.save_load_error)
418 message = details 413 message = details
419 } 414 }
415
420 CoreError.ErrorUnknown -> { 416 CoreError.ErrorUnknown -> {
421 title = emulationActivity.getString(R.string.fatal_error) 417 title = emulationActivity.getString(R.string.fatal_error)
422 message = emulationActivity.getString(R.string.fatal_error_message) 418 message = emulationActivity.getString(R.string.fatal_error_message)
423 } 419 }
420
424 else -> { 421 else -> {
425 return true 422 return true
426 } 423 }
@@ -454,6 +451,7 @@ object NativeLibrary {
454 captionId = R.string.loader_error_video_core 451 captionId = R.string.loader_error_video_core
455 descriptionId = R.string.loader_error_video_core_description 452 descriptionId = R.string.loader_error_video_core_description
456 } 453 }
454
457 else -> { 455 else -> {
458 captionId = R.string.loader_error_encrypted 456 captionId = R.string.loader_error_encrypted
459 descriptionId = R.string.loader_error_encrypted_roms_description 457 descriptionId = R.string.loader_error_encrypted_roms_description
@@ -465,7 +463,7 @@ object NativeLibrary {
465 463
466 val emulationActivity = sEmulationActivity.get() 464 val emulationActivity = sEmulationActivity.get()
467 if (emulationActivity == null) { 465 if (emulationActivity == null) {
468 warning("[NativeLibrary] EmulationActivity is null, can't exit.") 466 Log.warning("[NativeLibrary] EmulationActivity is null, can't exit.")
469 return 467 return
470 } 468 }
471 469
@@ -490,15 +488,27 @@ object NativeLibrary {
490 } 488 }
491 489
492 fun setEmulationActivity(emulationActivity: EmulationActivity?) { 490 fun setEmulationActivity(emulationActivity: EmulationActivity?) {
493 verbose("[NativeLibrary] Registering EmulationActivity.") 491 Log.verbose("[NativeLibrary] Registering EmulationActivity.")
494 sEmulationActivity = WeakReference(emulationActivity) 492 sEmulationActivity = WeakReference(emulationActivity)
495 } 493 }
496 494
497 fun clearEmulationActivity() { 495 fun clearEmulationActivity() {
498 verbose("[NativeLibrary] Unregistering EmulationActivity.") 496 Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
499 sEmulationActivity.clear() 497 sEmulationActivity.clear()
500 } 498 }
501 499
500 @Keep
501 @JvmStatic
502 fun onEmulationStarted() {
503 sEmulationActivity.get()!!.onEmulationStarted()
504 }
505
506 @Keep
507 @JvmStatic
508 fun onEmulationStopped(status: Int) {
509 sEmulationActivity.get()!!.onEmulationStopped(status)
510 }
511
502 /** 512 /**
503 * Logs the Yuzu version, Android version and, CPU. 513 * Logs the Yuzu version, Android version and, CPU.
504 */ 514 */
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index 04ab6a220..9561748cb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -46,7 +46,7 @@ class YuzuApplication : Application() {
46 super.onCreate() 46 super.onCreate()
47 application = this 47 application = this
48 documentsTree = DocumentsTree() 48 documentsTree = DocumentsTree()
49 DirectoryInitialization.start(applicationContext) 49 DirectoryInitialization.start()
50 GpuDriverHelper.initializeDriverParameters(applicationContext) 50 GpuDriverHelper.initializeDriverParameters(applicationContext)
51 NativeLibrary.logDeviceInfo() 51 NativeLibrary.logDeviceInfo()
52 52
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 7461fb093..bbd328c71 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -42,7 +42,7 @@ import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
42import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 42import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
43import org.yuzu.yuzu_emu.features.settings.model.IntSetting 43import org.yuzu.yuzu_emu.features.settings.model.IntSetting
44import org.yuzu.yuzu_emu.features.settings.model.Settings 44import org.yuzu.yuzu_emu.features.settings.model.Settings
45import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel 45import org.yuzu.yuzu_emu.model.EmulationViewModel
46import org.yuzu.yuzu_emu.model.Game 46import org.yuzu.yuzu_emu.model.Game
47import org.yuzu.yuzu_emu.utils.ControllerMappingHelper 47import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
48import org.yuzu.yuzu_emu.utils.ForegroundService 48import org.yuzu.yuzu_emu.utils.ForegroundService
@@ -72,18 +72,17 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
72 private val actionMute = "ACTION_EMULATOR_MUTE" 72 private val actionMute = "ACTION_EMULATOR_MUTE"
73 private val actionUnmute = "ACTION_EMULATOR_UNMUTE" 73 private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
74 74
75 private val settingsViewModel: SettingsViewModel by viewModels() 75 private val emulationViewModel: EmulationViewModel by viewModels()
76 76
77 override fun onDestroy() { 77 override fun onDestroy() {
78 stopForegroundService(this) 78 stopForegroundService(this)
79 emulationViewModel.clear()
79 super.onDestroy() 80 super.onDestroy()
80 } 81 }
81 82
82 override fun onCreate(savedInstanceState: Bundle?) { 83 override fun onCreate(savedInstanceState: Bundle?) {
83 ThemeHelper.setTheme(this) 84 ThemeHelper.setTheme(this)
84 85
85 settingsViewModel.settings.loadSettings()
86
87 super.onCreate(savedInstanceState) 86 super.onCreate(savedInstanceState)
88 87
89 binding = ActivityEmulationBinding.inflate(layoutInflater) 88 binding = ActivityEmulationBinding.inflate(layoutInflater)
@@ -91,9 +90,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
91 90
92 val navHostFragment = 91 val navHostFragment =
93 supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment 92 supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
94 val navController = navHostFragment.navController 93 navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
95 navController
96 .setGraph(R.navigation.emulation_navigation, intent.extras)
97 94
98 isActivityRecreated = savedInstanceState != null 95 isActivityRecreated = savedInstanceState != null
99 96
@@ -424,6 +421,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
424 } 421 }
425 } 422 }
426 423
424 fun onEmulationStarted() {
425 emulationViewModel.setEmulationStarted(true)
426 }
427
428 fun onEmulationStopped(status: Int) {
429 if (status == 0) {
430 finish()
431 }
432 }
433
427 private fun startMotionSensorListener() { 434 private fun startMotionSensorListener() {
428 val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager 435 val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
429 val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) 436 val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index e91277d35..0013e8512 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -3,8 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.adapters 4package org.yuzu.yuzu_emu.adapters
5 5
6import android.graphics.Bitmap 6import android.content.Intent
7import android.graphics.BitmapFactory 7import android.graphics.drawable.BitmapDrawable
8import android.net.Uri 8import android.net.Uri
9import android.text.TextUtils 9import android.text.TextUtils
10import android.view.LayoutInflater 10import android.view.LayoutInflater
@@ -13,25 +13,26 @@ import android.view.ViewGroup
13import android.widget.ImageView 13import android.widget.ImageView
14import android.widget.Toast 14import android.widget.Toast
15import androidx.appcompat.app.AppCompatActivity 15import androidx.appcompat.app.AppCompatActivity
16import androidx.core.content.pm.ShortcutInfoCompat
17import androidx.core.content.pm.ShortcutManagerCompat
18import androidx.core.graphics.drawable.IconCompat
16import androidx.documentfile.provider.DocumentFile 19import androidx.documentfile.provider.DocumentFile
17import androidx.lifecycle.ViewModelProvider 20import androidx.lifecycle.ViewModelProvider
18import androidx.lifecycle.lifecycleScope
19import androidx.navigation.findNavController 21import androidx.navigation.findNavController
20import androidx.preference.PreferenceManager 22import androidx.preference.PreferenceManager
21import androidx.recyclerview.widget.AsyncDifferConfig 23import androidx.recyclerview.widget.AsyncDifferConfig
22import androidx.recyclerview.widget.DiffUtil 24import androidx.recyclerview.widget.DiffUtil
23import androidx.recyclerview.widget.ListAdapter 25import androidx.recyclerview.widget.ListAdapter
24import androidx.recyclerview.widget.RecyclerView 26import androidx.recyclerview.widget.RecyclerView
25import coil.load
26import kotlinx.coroutines.launch
27import org.yuzu.yuzu_emu.HomeNavigationDirections 27import org.yuzu.yuzu_emu.HomeNavigationDirections
28import org.yuzu.yuzu_emu.NativeLibrary
29import org.yuzu.yuzu_emu.R 28import org.yuzu.yuzu_emu.R
30import org.yuzu.yuzu_emu.YuzuApplication 29import org.yuzu.yuzu_emu.YuzuApplication
30import org.yuzu.yuzu_emu.activities.EmulationActivity
31import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder 31import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
32import org.yuzu.yuzu_emu.databinding.CardGameBinding 32import org.yuzu.yuzu_emu.databinding.CardGameBinding
33import org.yuzu.yuzu_emu.model.Game 33import org.yuzu.yuzu_emu.model.Game
34import org.yuzu.yuzu_emu.model.GamesViewModel 34import org.yuzu.yuzu_emu.model.GamesViewModel
35import org.yuzu.yuzu_emu.utils.GameIconUtils
35 36
36class GameAdapter(private val activity: AppCompatActivity) : 37class GameAdapter(private val activity: AppCompatActivity) :
37 ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()), 38 ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
@@ -82,6 +83,21 @@ class GameAdapter(private val activity: AppCompatActivity) :
82 ) 83 )
83 .apply() 84 .apply()
84 85
86 val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
87 action = Intent.ACTION_VIEW
88 data = Uri.parse(holder.game.path)
89 }
90 val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
91 .setShortLabel(holder.game.title)
92 .setIcon(
93 IconCompat.createWithBitmap(
94 (holder.binding.imageGameScreen.drawable as BitmapDrawable).bitmap
95 )
96 )
97 .setIntent(openIntent)
98 .build()
99 ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
100
85 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) 101 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
86 view.findNavController().navigate(action) 102 view.findNavController().navigate(action)
87 } 103 }
@@ -98,12 +114,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
98 this.game = game 114 this.game = game
99 115
100 binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP 116 binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
101 activity.lifecycleScope.launch { 117 GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
102 val bitmap = decodeGameIcon(game.path)
103 binding.imageGameScreen.load(bitmap) {
104 error(R.drawable.default_icon)
105 }
106 }
107 118
108 binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ") 119 binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
109 120
@@ -126,14 +137,4 @@ class GameAdapter(private val activity: AppCompatActivity) :
126 return oldItem == newItem 137 return oldItem == newItem
127 } 138 }
128 } 139 }
129
130 private fun decodeGameIcon(uri: String): Bitmap? {
131 val data = NativeLibrary.getIcon(uri)
132 return BitmapFactory.decodeByteArray(
133 data,
134 0,
135 data.size,
136 BitmapFactory.Options()
137 )
138 }
139} 140}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index 9f859b442..8d87d3bd7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -45,8 +45,8 @@ class HomeSettingAdapter(
45 holder.option.onClick.invoke() 45 holder.option.onClick.invoke()
46 } else { 46 } else {
47 MessageDialogFragment.newInstance( 47 MessageDialogFragment.newInstance(
48 holder.option.disabledTitleId, 48 titleId = holder.option.disabledTitleId,
49 holder.option.disabledMessageId 49 descriptionId = holder.option.disabledMessageId
50 ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) 50 ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
51 } 51 }
52 } 52 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
index a18efef19..6f4b5b13f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
@@ -4,43 +4,43 @@
4package org.yuzu.yuzu_emu.disk_shader_cache 4package org.yuzu.yuzu_emu.disk_shader_cache
5 5
6import androidx.annotation.Keep 6import androidx.annotation.Keep
7import androidx.lifecycle.ViewModelProvider
7import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.disk_shader_cache.ui.ShaderProgressDialogFragment 10import org.yuzu.yuzu_emu.activities.EmulationActivity
11import org.yuzu.yuzu_emu.model.EmulationViewModel
12import org.yuzu.yuzu_emu.utils.Log
10 13
11@Keep 14@Keep
12object DiskShaderCacheProgress { 15object DiskShaderCacheProgress {
13 val finishLock = Object() 16 private lateinit var emulationViewModel: EmulationViewModel
14 private lateinit var fragment: ShaderProgressDialogFragment
15 17
16 private fun prepareDialog() { 18 private fun prepareViewModel() {
17 val emulationActivity = NativeLibrary.sEmulationActivity.get()!! 19 emulationViewModel =
18 emulationActivity.runOnUiThread { 20 ViewModelProvider(
19 fragment = ShaderProgressDialogFragment.newInstance( 21 NativeLibrary.sEmulationActivity.get() as EmulationActivity
20 emulationActivity.getString(R.string.loading), 22 )[EmulationViewModel::class.java]
21 emulationActivity.getString(R.string.preparing_shaders)
22 )
23 fragment.show(
24 emulationActivity.supportFragmentManager,
25 ShaderProgressDialogFragment.TAG
26 )
27 }
28 synchronized(finishLock) { finishLock.wait() }
29 } 23 }
30 24
31 @JvmStatic 25 @JvmStatic
32 fun loadProgress(stage: Int, progress: Int, max: Int) { 26 fun loadProgress(stage: Int, progress: Int, max: Int) {
33 val emulationActivity = NativeLibrary.sEmulationActivity.get() 27 val emulationActivity = NativeLibrary.sEmulationActivity.get()
34 ?: error("[DiskShaderCacheProgress] EmulationActivity not present") 28 if (emulationActivity == null) {
35 29 Log.error("[DiskShaderCacheProgress] EmulationActivity not present")
36 when (LoadCallbackStage.values()[stage]) { 30 return
37 LoadCallbackStage.Prepare -> prepareDialog() 31 }
38 LoadCallbackStage.Build -> fragment.onUpdateProgress( 32
39 emulationActivity.getString(R.string.building_shaders), 33 emulationActivity.runOnUiThread {
40 progress, 34 when (LoadCallbackStage.values()[stage]) {
41 max 35 LoadCallbackStage.Prepare -> prepareViewModel()
42 ) 36 LoadCallbackStage.Build -> emulationViewModel.updateProgress(
43 LoadCallbackStage.Complete -> fragment.dismiss() 37 emulationActivity.getString(R.string.building_shaders),
38 progress,
39 max
40 )
41
42 LoadCallbackStage.Complete -> {}
43 }
44 } 44 }
45 } 45 }
46 46
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt
deleted file mode 100644
index bf6f0366d..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt
+++ /dev/null
@@ -1,31 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.disk_shader_cache
5
6import androidx.lifecycle.LiveData
7import androidx.lifecycle.MutableLiveData
8import androidx.lifecycle.ViewModel
9
10class ShaderProgressViewModel : ViewModel() {
11 private val _progress = MutableLiveData(0)
12 val progress: LiveData<Int> get() = _progress
13
14 private val _max = MutableLiveData(0)
15 val max: LiveData<Int> get() = _max
16
17 private val _message = MutableLiveData("")
18 val message: LiveData<String> get() = _message
19
20 fun setProgress(progress: Int) {
21 _progress.postValue(progress)
22 }
23
24 fun setMax(max: Int) {
25 _max.postValue(max)
26 }
27
28 fun setMessage(msg: String) {
29 _message.postValue(msg)
30 }
31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
deleted file mode 100644
index 8a8e0a6e8..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
+++ /dev/null
@@ -1,103 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.disk_shader_cache.ui
5
6import android.app.Dialog
7import android.os.Bundle
8import android.view.LayoutInflater
9import android.view.View
10import android.view.ViewGroup
11import androidx.appcompat.app.AlertDialog
12import androidx.fragment.app.DialogFragment
13import androidx.lifecycle.ViewModelProvider
14import com.google.android.material.dialog.MaterialAlertDialogBuilder
15import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
16import org.yuzu.yuzu_emu.disk_shader_cache.DiskShaderCacheProgress
17import org.yuzu.yuzu_emu.disk_shader_cache.ShaderProgressViewModel
18
19class ShaderProgressDialogFragment : DialogFragment() {
20 private var _binding: DialogProgressBarBinding? = null
21 private val binding get() = _binding!!
22
23 private lateinit var alertDialog: AlertDialog
24
25 private lateinit var shaderProgressViewModel: ShaderProgressViewModel
26
27 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
28 _binding = DialogProgressBarBinding.inflate(layoutInflater)
29 shaderProgressViewModel =
30 ViewModelProvider(requireActivity())[ShaderProgressViewModel::class.java]
31
32 val title = requireArguments().getString(TITLE)
33 val message = requireArguments().getString(MESSAGE)
34
35 isCancelable = false
36 alertDialog = MaterialAlertDialogBuilder(requireActivity())
37 .setView(binding.root)
38 .setTitle(title)
39 .setMessage(message)
40 .create()
41 return alertDialog
42 }
43
44 override fun onCreateView(
45 inflater: LayoutInflater,
46 container: ViewGroup?,
47 savedInstanceState: Bundle?
48 ): View {
49 return binding.root
50 }
51
52 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
53 super.onViewCreated(view, savedInstanceState)
54 shaderProgressViewModel.progress.observe(viewLifecycleOwner) { progress ->
55 binding.progressBar.progress = progress
56 setUpdateText()
57 }
58 shaderProgressViewModel.max.observe(viewLifecycleOwner) { max ->
59 binding.progressBar.max = max
60 setUpdateText()
61 }
62 shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
63 alertDialog.setMessage(msg)
64 }
65 synchronized(DiskShaderCacheProgress.finishLock) {
66 DiskShaderCacheProgress.finishLock.notifyAll()
67 }
68 }
69
70 override fun onDestroyView() {
71 super.onDestroyView()
72 _binding = null
73 }
74
75 fun onUpdateProgress(msg: String, progress: Int, max: Int) {
76 shaderProgressViewModel.setProgress(progress)
77 shaderProgressViewModel.setMax(max)
78 shaderProgressViewModel.setMessage(msg)
79 }
80
81 private fun setUpdateText() {
82 binding.progressText.text = String.format(
83 "%d/%d",
84 shaderProgressViewModel.progress.value,
85 shaderProgressViewModel.max.value
86 )
87 }
88
89 companion object {
90 const val TAG = "ProgressDialogFragment"
91 const val TITLE = "title"
92 const val MESSAGE = "message"
93
94 fun newInstance(title: String, message: String): ShaderProgressDialogFragment {
95 val frag = ShaderProgressDialogFragment()
96 val args = Bundle()
97 args.putString(TITLE, title)
98 args.putString(MESSAGE, message)
99 frag.arguments = args
100 return frag
101 }
102 }
103}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
index a6e9833ee..aeda8d222 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractBooleanSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractBooleanSetting : AbstractSetting { 6interface AbstractBooleanSetting : AbstractSetting {
7 var boolean: Boolean 7 val boolean: Boolean
8
9 fun setBoolean(value: Boolean)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
index bd9233d62..606519ad8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingsViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractByteSetting.kt
@@ -3,8 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import androidx.lifecycle.ViewModel 6interface AbstractByteSetting : AbstractSetting {
7 val byte: Byte
7 8
8class SettingsViewModel : ViewModel() { 9 fun setByte(value: Byte)
9 val settings = Settings()
10} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
index 6fe4bc263..974925eed 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractFloatSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractFloatSetting : AbstractSetting { 6interface AbstractFloatSetting : AbstractSetting {
7 var float: Float 7 val float: Float
8
9 fun setFloat(value: Float)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
index 892b7dcfe..89b285b10 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractIntSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractIntSetting : AbstractSetting { 6interface AbstractIntSetting : AbstractSetting {
7 var int: Int 7 val int: Int
8
9 fun setInt(value: Int)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
new file mode 100644
index 000000000..4873942db
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractLongSetting.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6interface AbstractLongSetting : AbstractSetting {
7 val long: Long
8
9 fun setLong(value: Long)
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
index 258580209..8b6d29fe5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractSetting.kt
@@ -3,10 +3,22 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6interface AbstractSetting { 8interface AbstractSetting {
7 val key: String? 9 val key: String
8 val section: String? 10 val category: Settings.Category
9 val isRuntimeEditable: Boolean
10 val valueAsString: String
11 val defaultValue: Any 11 val defaultValue: Any
12 val androidDefault: Any?
13 get() = null
14 val valueAsString: String
15 get() = ""
16
17 val isRuntimeModifiable: Boolean
18 get() = NativeConfig.getIsRuntimeModifiable(key)
19
20 val pairedSettingKey: String
21 get() = NativeConfig.getPairedSettingKey(key)
22
23 fun reset()
12} 24}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
new file mode 100644
index 000000000..91407ccbb
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractShortSetting.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6interface AbstractShortSetting : AbstractSetting {
7 val short: Short
8
9 fun setShort(value: Short)
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
index 0d02c5997..c8935cc48 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/AbstractStringSetting.kt
@@ -4,5 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6interface AbstractStringSetting : AbstractSetting { 6interface AbstractStringSetting : AbstractSetting {
7 var string: String 7 val string: String
8
9 fun setString(value: String)
8} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index d41933766..e0c0538c7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -3,41 +3,37 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class BooleanSetting( 8enum class BooleanSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category,
9 override val defaultValue: Boolean 11 override val androidDefault: Boolean? = null
10) : AbstractBooleanSetting { 12) : AbstractBooleanSetting {
11 CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false), 13 CPU_DEBUG_MODE("cpu_debug_mode", Settings.Category.Cpu),
12 FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true), 14 FASTMEM("cpuopt_fastmem", Settings.Category.Cpu),
13 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true), 15 FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.Category.Cpu),
14 PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true), 16 RENDERER_USE_SPEED_LIMIT("use_speed_limit", Settings.Category.Core),
15 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false); 17 USE_DOCKED_MODE("use_docked_mode", Settings.Category.System, false),
16 18 RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache", Settings.Category.Renderer),
17 override var boolean: Boolean = defaultValue 19 RENDERER_FORCE_MAX_CLOCK("force_max_clock", Settings.Category.Renderer),
20 RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders", Settings.Category.Renderer),
21 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing", Settings.Category.Renderer, false),
22 RENDERER_DEBUG("debug", Settings.Category.Renderer),
23 PICTURE_IN_PICTURE("picture_in_picture", Settings.Category.Android),
24 USE_CUSTOM_RTC("custom_rtc_enabled", Settings.Category.System);
25
26 override val boolean: Boolean
27 get() = NativeConfig.getBoolean(key, false)
28
29 override fun setBoolean(value: Boolean) = NativeConfig.setBoolean(key, value)
30
31 override val defaultValue: Boolean by lazy {
32 androidDefault ?: NativeConfig.getBoolean(key, true)
33 }
18 34
19 override val valueAsString: String 35 override val valueAsString: String
20 get() = boolean.toString() 36 get() = if (boolean) "1" else "0"
21 37
22 override val isRuntimeEditable: Boolean 38 override fun reset() = NativeConfig.setBoolean(key, defaultValue)
23 get() {
24 for (setting in NOT_RUNTIME_EDITABLE) {
25 if (setting == this) {
26 return false
27 }
28 }
29 return true
30 }
31
32 companion object {
33 private val NOT_RUNTIME_EDITABLE = listOf(
34 PICTURE_IN_PICTURE,
35 USE_CUSTOM_RTC
36 )
37
38 fun from(key: String): BooleanSetting? =
39 BooleanSetting.values().firstOrNull { it.key == key }
40
41 fun clear() = BooleanSetting.values().forEach { it.boolean = it.defaultValue }
42 }
43} 39}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
new file mode 100644
index 000000000..6ec0a765e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ByteSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class ByteSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractByteSetting {
12 AUDIO_VOLUME("volume", Settings.Category.Audio);
13
14 override val byte: Byte
15 get() = NativeConfig.getByte(key, false)
16
17 override fun setByte(value: Byte) = NativeConfig.setByte(key, value)
18
19 override val defaultValue: Byte by lazy { NativeConfig.getByte(key, true) }
20
21 override val valueAsString: String
22 get() = byte.toString()
23
24 override fun reset() = NativeConfig.setByte(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
index e5545a916..0181d06f2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/FloatSetting.kt
@@ -3,34 +3,24 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class FloatSetting( 8enum class FloatSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: Float
10) : AbstractFloatSetting { 11) : AbstractFloatSetting {
11 // No float settings currently exist 12 // No float settings currently exist
12 EMPTY_SETTING("", "", 0f); 13 EMPTY_SETTING("", Settings.Category.UiGeneral);
13
14 override var float: Float = defaultValue
15 14
16 override val valueAsString: String 15 override val float: Float
17 get() = float.toString() 16 get() = NativeConfig.getFloat(key, false)
18 17
19 override val isRuntimeEditable: Boolean 18 override fun setFloat(value: Float) = NativeConfig.setFloat(key, value)
20 get() {
21 for (setting in NOT_RUNTIME_EDITABLE) {
22 if (setting == this) {
23 return false
24 }
25 }
26 return true
27 }
28 19
29 companion object { 20 override val defaultValue: Float by lazy { NativeConfig.getFloat(key, true) }
30 private val NOT_RUNTIME_EDITABLE = emptyList<FloatSetting>()
31 21
32 fun from(key: String): FloatSetting? = FloatSetting.values().firstOrNull { it.key == key } 22 override val valueAsString: String
23 get() = float.toString()
33 24
34 fun clear() = FloatSetting.values().forEach { it.float = it.defaultValue } 25 override fun reset() = NativeConfig.setFloat(key, defaultValue)
35 }
36} 26}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 4427a7d9d..151362124 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -3,139 +3,37 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class IntSetting( 8enum class IntSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category,
9 override val defaultValue: Int 11 override val androidDefault: Int? = null
10) : AbstractIntSetting { 12) : AbstractIntSetting {
11 RENDERER_USE_SPEED_LIMIT( 13 CPU_ACCURACY("cpu_accuracy", Settings.Category.Cpu),
12 "use_speed_limit", 14 REGION_INDEX("region_index", Settings.Category.System),
13 Settings.SECTION_RENDERER, 15 LANGUAGE_INDEX("language_index", Settings.Category.System),
14 1 16 RENDERER_BACKEND("backend", Settings.Category.Renderer),
15 ), 17 RENDERER_ACCURACY("gpu_accuracy", Settings.Category.Renderer, 0),
16 USE_DOCKED_MODE( 18 RENDERER_RESOLUTION("resolution_setup", Settings.Category.Renderer),
17 "use_docked_mode", 19 RENDERER_VSYNC("use_vsync", Settings.Category.Renderer),
18 Settings.SECTION_SYSTEM, 20 RENDERER_SCALING_FILTER("scaling_filter", Settings.Category.Renderer),
19 0 21 RENDERER_ANTI_ALIASING("anti_aliasing", Settings.Category.Renderer),
20 ), 22 RENDERER_SCREEN_LAYOUT("screen_layout", Settings.Category.Android),
21 RENDERER_USE_DISK_SHADER_CACHE( 23 RENDERER_ASPECT_RATIO("aspect_ratio", Settings.Category.Renderer),
22 "use_disk_shader_cache", 24 AUDIO_OUTPUT_ENGINE("output_engine", Settings.Category.Audio);
23 Settings.SECTION_RENDERER, 25
24 1 26 override val int: Int
25 ), 27 get() = NativeConfig.getInt(key, false)
26 RENDERER_FORCE_MAX_CLOCK( 28
27 "force_max_clock", 29 override fun setInt(value: Int) = NativeConfig.setInt(key, value)
28 Settings.SECTION_RENDERER, 30
29 0 31 override val defaultValue: Int by lazy {
30 ), 32 androidDefault ?: NativeConfig.getInt(key, true)
31 RENDERER_ASYNCHRONOUS_SHADERS( 33 }
32 "use_asynchronous_shaders",
33 Settings.SECTION_RENDERER,
34 0
35 ),
36 RENDERER_REACTIVE_FLUSHING(
37 "use_reactive_flushing",
38 Settings.SECTION_RENDERER,
39 0
40 ),
41 RENDERER_DEBUG(
42 "debug",
43 Settings.SECTION_RENDERER,
44 0
45 ),
46 RENDERER_SPEED_LIMIT(
47 "speed_limit",
48 Settings.SECTION_RENDERER,
49 100
50 ),
51 CPU_ACCURACY(
52 "cpu_accuracy",
53 Settings.SECTION_CPU,
54 0
55 ),
56 REGION_INDEX(
57 "region_index",
58 Settings.SECTION_SYSTEM,
59 -1
60 ),
61 LANGUAGE_INDEX(
62 "language_index",
63 Settings.SECTION_SYSTEM,
64 1
65 ),
66 RENDERER_BACKEND(
67 "backend",
68 Settings.SECTION_RENDERER,
69 1
70 ),
71 RENDERER_ACCURACY(
72 "gpu_accuracy",
73 Settings.SECTION_RENDERER,
74 0
75 ),
76 RENDERER_RESOLUTION(
77 "resolution_setup",
78 Settings.SECTION_RENDERER,
79 2
80 ),
81 RENDERER_VSYNC(
82 "use_vsync",
83 Settings.SECTION_RENDERER,
84 0
85 ),
86 RENDERER_SCALING_FILTER(
87 "scaling_filter",
88 Settings.SECTION_RENDERER,
89 1
90 ),
91 RENDERER_ANTI_ALIASING(
92 "anti_aliasing",
93 Settings.SECTION_RENDERER,
94 0
95 ),
96 RENDERER_SCREEN_LAYOUT(
97 "screen_layout",
98 Settings.SECTION_RENDERER,
99 Settings.LayoutOption_MobileLandscape
100 ),
101 RENDERER_ASPECT_RATIO(
102 "aspect_ratio",
103 Settings.SECTION_RENDERER,
104 0
105 ),
106 AUDIO_VOLUME(
107 "volume",
108 Settings.SECTION_AUDIO,
109 100
110 );
111
112 override var int: Int = defaultValue
113 34
114 override val valueAsString: String 35 override val valueAsString: String
115 get() = int.toString() 36 get() = int.toString()
116 37
117 override val isRuntimeEditable: Boolean 38 override fun reset() = NativeConfig.setInt(key, defaultValue)
118 get() {
119 for (setting in NOT_RUNTIME_EDITABLE) {
120 if (setting == this) {
121 return false
122 }
123 }
124 return true
125 }
126
127 companion object {
128 private val NOT_RUNTIME_EDITABLE = listOf(
129 RENDERER_USE_DISK_SHADER_CACHE,
130 RENDERER_ASYNCHRONOUS_SHADERS,
131 RENDERER_DEBUG,
132 RENDERER_BACKEND,
133 RENDERER_RESOLUTION,
134 RENDERER_VSYNC
135 )
136
137 fun from(key: String): IntSetting? = IntSetting.values().firstOrNull { it.key == key }
138
139 fun clear() = IntSetting.values().forEach { it.int = it.defaultValue }
140 }
141} 39}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
new file mode 100644
index 000000000..c526fc4cf
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/LongSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class LongSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractLongSetting {
12 CUSTOM_RTC("custom_rtc", Settings.Category.System);
13
14 override val long: Long
15 get() = NativeConfig.getLong(key, false)
16
17 override fun setLong(value: Long) = NativeConfig.setLong(key, value)
18
19 override val defaultValue: Long by lazy { NativeConfig.getLong(key, true) }
20
21 override val valueAsString: String
22 get() = long.toString()
23
24 override fun reset() = NativeConfig.setLong(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
deleted file mode 100644
index 474f598a9..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/SettingSection.kt
+++ /dev/null
@@ -1,37 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6/**
7 * A semantically-related group of Settings objects. These Settings are
8 * internally stored as a HashMap.
9 */
10class SettingSection(val name: String) {
11 val settings = HashMap<String, AbstractSetting>()
12
13 /**
14 * Convenience method; inserts a value directly into the backing HashMap.
15 *
16 * @param setting The Setting to be inserted.
17 */
18 fun putSetting(setting: AbstractSetting) {
19 settings[setting.key!!] = setting
20 }
21
22 /**
23 * Convenience method; gets a value directly from the backing HashMap.
24 *
25 * @param key Used to retrieve the Setting.
26 * @return A Setting object (you should probably cast this before using)
27 */
28 fun getSetting(key: String): AbstractSetting? {
29 return settings[key]
30 }
31
32 fun mergeSection(settingSection: SettingSection) {
33 for (setting in settingSection.settings.values) {
34 putSetting(setting)
35 }
36 }
37}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index a6251bafd..0702236e8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -4,195 +4,151 @@
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import android.text.TextUtils 6import android.text.TextUtils
7import java.util.* 7import android.widget.Toast
8import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
11import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 10import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
12 11
13class Settings { 12object Settings {
14 private var gameId: String? = null 13 private val context get() = YuzuApplication.appContext
15 14
16 var isLoaded = false 15 fun saveSettings(gameId: String = "") {
17
18 /**
19 * A HashMap<String></String>, SettingSection> that constructs a new SettingSection instead of returning null
20 * when getting a key not already in the map
21 */
22 class SettingsSectionMap : HashMap<String, SettingSection?>() {
23 override operator fun get(key: String): SettingSection? {
24 if (!super.containsKey(key)) {
25 val section = SettingSection(key)
26 super.put(key, section)
27 return section
28 }
29 return super.get(key)
30 }
31 }
32
33 var sections: HashMap<String, SettingSection?> = SettingsSectionMap()
34
35 fun getSection(sectionName: String): SettingSection? {
36 return sections[sectionName]
37 }
38
39 val isEmpty: Boolean
40 get() = sections.isEmpty()
41
42 fun loadSettings(view: SettingsActivityView? = null) {
43 sections = SettingsSectionMap()
44 loadYuzuSettings(view)
45 if (!TextUtils.isEmpty(gameId)) {
46 loadCustomGameSettings(gameId!!, view)
47 }
48 isLoaded = true
49 }
50
51 private fun loadYuzuSettings(view: SettingsActivityView?) {
52 for ((fileName) in configFileSectionsMap) {
53 sections.putAll(SettingsFile.readFile(fileName, view))
54 }
55 }
56
57 private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView?) {
58 // Custom game settings
59 mergeSections(SettingsFile.readCustomGameSettings(gameId, view))
60 }
61
62 private fun mergeSections(updatedSections: HashMap<String, SettingSection?>) {
63 for ((key, updatedSection) in updatedSections) {
64 if (sections.containsKey(key)) {
65 val originalSection = sections[key]
66 originalSection!!.mergeSection(updatedSection!!)
67 } else {
68 sections[key] = updatedSection
69 }
70 }
71 }
72
73 fun loadSettings(gameId: String, view: SettingsActivityView) {
74 this.gameId = gameId
75 loadSettings(view)
76 }
77
78 fun saveSettings(view: SettingsActivityView) {
79 if (TextUtils.isEmpty(gameId)) { 16 if (TextUtils.isEmpty(gameId)) {
80 view.showToastMessage( 17 Toast.makeText(
81 YuzuApplication.appContext.getString(R.string.ini_saved), 18 context,
82 false 19 context.getString(R.string.ini_saved),
83 ) 20 Toast.LENGTH_SHORT
84 21 ).show()
85 for ((fileName, sectionNames) in configFileSectionsMap) { 22 SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG)
86 val iniSections = TreeMap<String, SettingSection>()
87 for (section in sectionNames) {
88 iniSections[section] = sections[section]!!
89 }
90
91 SettingsFile.saveFile(fileName, iniSections, view)
92 }
93 } else { 23 } else {
94 // Custom game settings 24 // TODO: Save custom game settings
95 view.showToastMessage( 25 Toast.makeText(
96 YuzuApplication.appContext.getString(R.string.gameid_saved, gameId), 26 context,
97 false 27 context.getString(R.string.gameid_saved, gameId),
98 ) 28 Toast.LENGTH_SHORT
99 29 ).show()
100 SettingsFile.saveCustomGameSettings(gameId, sections)
101 } 30 }
102 } 31 }
103 32
104 companion object { 33 enum class Category {
105 const val SECTION_GENERAL = "General" 34 Android,
106 const val SECTION_SYSTEM = "System" 35 Audio,
107 const val SECTION_RENDERER = "Renderer" 36 Core,
108 const val SECTION_AUDIO = "Audio" 37 Cpu,
109 const val SECTION_CPU = "Cpu" 38 CpuDebug,
110 const val SECTION_THEME = "Theme" 39 CpuUnsafe,
111 const val SECTION_DEBUG = "Debug" 40 Renderer,
112 41 RendererAdvanced,
113 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 42 RendererDebug,
114 43 System,
115 const val PREF_OVERLAY_VERSION = "OverlayVersion" 44 SystemAudio,
116 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion" 45 DataStorage,
117 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion" 46 Debugging,
118 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion" 47 DebuggingGraphics,
119 val overlayLayoutPrefs = listOf( 48 Miscellaneous,
120 PREF_LANDSCAPE_OVERLAY_VERSION, 49 Network,
121 PREF_PORTRAIT_OVERLAY_VERSION, 50 WebService,
122 PREF_FOLDABLE_OVERLAY_VERSION 51 AddOns,
123 ) 52 Controls,
124 53 Ui,
125 const val PREF_CONTROL_SCALE = "controlScale" 54 UiGeneral,
126 const val PREF_CONTROL_OPACITY = "controlOpacity" 55 UiLayout,
127 const val PREF_TOUCH_ENABLED = "isTouchEnabled" 56 UiGameList,
128 const val PREF_BUTTON_A = "buttonToggle0" 57 Screenshots,
129 const val PREF_BUTTON_B = "buttonToggle1" 58 Shortcuts,
130 const val PREF_BUTTON_X = "buttonToggle2" 59 Multiplayer,
131 const val PREF_BUTTON_Y = "buttonToggle3" 60 Services,
132 const val PREF_BUTTON_L = "buttonToggle4" 61 Paths,
133 const val PREF_BUTTON_R = "buttonToggle5" 62 MaxEnum
134 const val PREF_BUTTON_ZL = "buttonToggle6"
135 const val PREF_BUTTON_ZR = "buttonToggle7"
136 const val PREF_BUTTON_PLUS = "buttonToggle8"
137 const val PREF_BUTTON_MINUS = "buttonToggle9"
138 const val PREF_BUTTON_DPAD = "buttonToggle10"
139 const val PREF_STICK_L = "buttonToggle11"
140 const val PREF_STICK_R = "buttonToggle12"
141 const val PREF_BUTTON_STICK_L = "buttonToggle13"
142 const val PREF_BUTTON_STICK_R = "buttonToggle14"
143 const val PREF_BUTTON_HOME = "buttonToggle15"
144 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
145
146 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
147 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
148 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
149 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
150 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
151
152 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
153 const val PREF_THEME = "Theme"
154 const val PREF_THEME_MODE = "ThemeMode"
155 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
156
157 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
158
159 val overlayPreferences = listOf(
160 PREF_OVERLAY_VERSION,
161 PREF_CONTROL_SCALE,
162 PREF_CONTROL_OPACITY,
163 PREF_TOUCH_ENABLED,
164 PREF_BUTTON_A,
165 PREF_BUTTON_B,
166 PREF_BUTTON_X,
167 PREF_BUTTON_Y,
168 PREF_BUTTON_L,
169 PREF_BUTTON_R,
170 PREF_BUTTON_ZL,
171 PREF_BUTTON_ZR,
172 PREF_BUTTON_PLUS,
173 PREF_BUTTON_MINUS,
174 PREF_BUTTON_DPAD,
175 PREF_STICK_L,
176 PREF_STICK_R,
177 PREF_BUTTON_HOME,
178 PREF_BUTTON_SCREENSHOT,
179 PREF_BUTTON_STICK_L,
180 PREF_BUTTON_STICK_R
181 )
182
183 const val LayoutOption_Unspecified = 0
184 const val LayoutOption_MobilePortrait = 4
185 const val LayoutOption_MobileLandscape = 5
186
187 init {
188 configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
189 listOf(
190 SECTION_GENERAL,
191 SECTION_SYSTEM,
192 SECTION_RENDERER,
193 SECTION_AUDIO,
194 SECTION_CPU
195 )
196 }
197 } 63 }
64
65 val settingsList = listOf<AbstractSetting>(
66 *BooleanSetting.values(),
67 *ByteSetting.values(),
68 *ShortSetting.values(),
69 *IntSetting.values(),
70 *FloatSetting.values(),
71 *LongSetting.values(),
72 *StringSetting.values()
73 )
74
75 const val SECTION_GENERAL = "General"
76 const val SECTION_SYSTEM = "System"
77 const val SECTION_RENDERER = "Renderer"
78 const val SECTION_AUDIO = "Audio"
79 const val SECTION_CPU = "Cpu"
80 const val SECTION_THEME = "Theme"
81 const val SECTION_DEBUG = "Debug"
82
83 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
84
85 const val PREF_OVERLAY_VERSION = "OverlayVersion"
86 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
87 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
88 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
89 val overlayLayoutPrefs = listOf(
90 PREF_LANDSCAPE_OVERLAY_VERSION,
91 PREF_PORTRAIT_OVERLAY_VERSION,
92 PREF_FOLDABLE_OVERLAY_VERSION
93 )
94
95 const val PREF_CONTROL_SCALE = "controlScale"
96 const val PREF_CONTROL_OPACITY = "controlOpacity"
97 const val PREF_TOUCH_ENABLED = "isTouchEnabled"
98 const val PREF_BUTTON_A = "buttonToggle0"
99 const val PREF_BUTTON_B = "buttonToggle1"
100 const val PREF_BUTTON_X = "buttonToggle2"
101 const val PREF_BUTTON_Y = "buttonToggle3"
102 const val PREF_BUTTON_L = "buttonToggle4"
103 const val PREF_BUTTON_R = "buttonToggle5"
104 const val PREF_BUTTON_ZL = "buttonToggle6"
105 const val PREF_BUTTON_ZR = "buttonToggle7"
106 const val PREF_BUTTON_PLUS = "buttonToggle8"
107 const val PREF_BUTTON_MINUS = "buttonToggle9"
108 const val PREF_BUTTON_DPAD = "buttonToggle10"
109 const val PREF_STICK_L = "buttonToggle11"
110 const val PREF_STICK_R = "buttonToggle12"
111 const val PREF_BUTTON_STICK_L = "buttonToggle13"
112 const val PREF_BUTTON_STICK_R = "buttonToggle14"
113 const val PREF_BUTTON_HOME = "buttonToggle15"
114 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
115
116 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
117 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
118 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
119 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
120 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
121
122 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
123 const val PREF_THEME = "Theme"
124 const val PREF_THEME_MODE = "ThemeMode"
125 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
126
127 val overlayPreferences = listOf(
128 PREF_OVERLAY_VERSION,
129 PREF_CONTROL_SCALE,
130 PREF_CONTROL_OPACITY,
131 PREF_TOUCH_ENABLED,
132 PREF_BUTTON_A,
133 PREF_BUTTON_B,
134 PREF_BUTTON_X,
135 PREF_BUTTON_Y,
136 PREF_BUTTON_L,
137 PREF_BUTTON_R,
138 PREF_BUTTON_ZL,
139 PREF_BUTTON_ZR,
140 PREF_BUTTON_PLUS,
141 PREF_BUTTON_MINUS,
142 PREF_BUTTON_DPAD,
143 PREF_STICK_L,
144 PREF_STICK_R,
145 PREF_BUTTON_HOME,
146 PREF_BUTTON_SCREENSHOT,
147 PREF_BUTTON_STICK_L,
148 PREF_BUTTON_STICK_R
149 )
150
151 const val LayoutOption_Unspecified = 0
152 const val LayoutOption_MobilePortrait = 4
153 const val LayoutOption_MobileLandscape = 5
198} 154}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
new file mode 100644
index 000000000..c9a0c664c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/ShortSetting.kt
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.model
5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
8enum class ShortSetting(
9 override val key: String,
10 override val category: Settings.Category
11) : AbstractShortSetting {
12 RENDERER_SPEED_LIMIT("speed_limit", Settings.Category.Core);
13
14 override val short: Short
15 get() = NativeConfig.getShort(key, false)
16
17 override fun setShort(value: Short) = NativeConfig.setShort(key, value)
18
19 override val defaultValue: Short by lazy { NativeConfig.getShort(key, true) }
20
21 override val valueAsString: String
22 get() = short.toString()
23
24 override fun reset() = NativeConfig.setShort(key, defaultValue)
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index 6621289fd..9bb3e66d4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -3,36 +3,24 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model 4package org.yuzu.yuzu_emu.features.settings.model
5 5
6import org.yuzu.yuzu_emu.utils.NativeConfig
7
6enum class StringSetting( 8enum class StringSetting(
7 override val key: String, 9 override val key: String,
8 override val section: String, 10 override val category: Settings.Category
9 override val defaultValue: String
10) : AbstractStringSetting { 11) : AbstractStringSetting {
11 AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"), 12 // No string settings currently exist
12 CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"); 13 EMPTY_SETTING("", Settings.Category.UiGeneral);
14
15 override val string: String
16 get() = NativeConfig.getString(key, false)
17
18 override fun setString(value: String) = NativeConfig.setString(key, value)
13 19
14 override var string: String = defaultValue 20 override val defaultValue: String by lazy { NativeConfig.getString(key, true) }
15 21
16 override val valueAsString: String 22 override val valueAsString: String
17 get() = string 23 get() = string
18 24
19 override val isRuntimeEditable: Boolean 25 override fun reset() = NativeConfig.setString(key, defaultValue)
20 get() {
21 for (setting in NOT_RUNTIME_EDITABLE) {
22 if (setting == this) {
23 return false
24 }
25 }
26 return true
27 }
28
29 companion object {
30 private val NOT_RUNTIME_EDITABLE = listOf(
31 CUSTOM_RTC
32 )
33
34 fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
35
36 fun clear() = StringSetting.values().forEach { it.string = it.defaultValue }
37 }
38} 26}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
index bc0bf7788..8bc164197 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/DateTimeSetting.kt
@@ -3,29 +3,16 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 6import org.yuzu.yuzu_emu.features.settings.model.AbstractLongSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
8 7
9class DateTimeSetting( 8class DateTimeSetting(
10 setting: AbstractSetting?, 9 private val longSetting: AbstractLongSetting,
11 titleId: Int, 10 titleId: Int,
12 descriptionId: Int, 11 descriptionId: Int
13 val key: String? = null, 12) : SettingsItem(longSetting, titleId, descriptionId) {
14 private val defaultValue: String? = null
15) : SettingsItem(setting, titleId, descriptionId) {
16 override val type = TYPE_DATETIME_SETTING 13 override val type = TYPE_DATETIME_SETTING
17 14
18 val value: String 15 var value: Long
19 get() = if (setting != null) { 16 get() = longSetting.long
20 val setting = setting as AbstractStringSetting 17 set(value) = (setting as AbstractLongSetting).setLong(value)
21 setting.string
22 } else {
23 defaultValue!!
24 }
25
26 fun setSelectedValue(datetime: String): AbstractStringSetting {
27 val stringSetting = setting as AbstractStringSetting
28 stringSetting.string = datetime
29 return stringSetting
30 }
31} 18}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
index a67001311..d31ce1c31 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/HeaderSetting.kt
@@ -5,6 +5,6 @@ package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6class HeaderSetting( 6class HeaderSetting(
7 titleId: Int 7 titleId: Int
8) : SettingsItem(null, titleId, 0) { 8) : SettingsItem(emptySetting, titleId, 0) {
9 override val type = TYPE_HEADER 9 override val type = TYPE_HEADER
10} 10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
index caaab50d8..522cc49df 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
@@ -8,6 +8,6 @@ class RunnableSetting(
8 descriptionId: Int, 8 descriptionId: Int,
9 val isRuntimeRunnable: Boolean, 9 val isRuntimeRunnable: Boolean,
10 val runnable: () -> Unit 10 val runnable: () -> Unit
11) : SettingsItem(null, titleId, descriptionId) { 11) : SettingsItem(emptySetting, titleId, descriptionId) {
12 override val type = TYPE_RUNNABLE 12 override val type = TYPE_RUNNABLE
13} 13}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 07520849e..b3b3fc209 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -4,7 +4,15 @@
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.NativeLibrary 6import org.yuzu.yuzu_emu.NativeLibrary
7import org.yuzu.yuzu_emu.R
8import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
10import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
11import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
12import org.yuzu.yuzu_emu.features.settings.model.IntSetting
13import org.yuzu.yuzu_emu.features.settings.model.LongSetting
14import org.yuzu.yuzu_emu.features.settings.model.Settings
15import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
8 16
9/** 17/**
10 * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. 18 * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
@@ -14,7 +22,7 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
14 * file.) 22 * file.)
15 */ 23 */
16abstract class SettingsItem( 24abstract class SettingsItem(
17 var setting: AbstractSetting?, 25 val setting: AbstractSetting,
18 val nameId: Int, 26 val nameId: Int,
19 val descriptionId: Int 27 val descriptionId: Int
20) { 28) {
@@ -23,7 +31,7 @@ abstract class SettingsItem(
23 val isEditable: Boolean 31 val isEditable: Boolean
24 get() { 32 get() {
25 if (!NativeLibrary.isRunning()) return true 33 if (!NativeLibrary.isRunning()) return true
26 return setting?.isRuntimeEditable ?: false 34 return setting.isRuntimeModifiable
27 } 35 }
28 36
29 companion object { 37 companion object {
@@ -35,5 +43,240 @@ abstract class SettingsItem(
35 const val TYPE_STRING_SINGLE_CHOICE = 5 43 const val TYPE_STRING_SINGLE_CHOICE = 5
36 const val TYPE_DATETIME_SETTING = 6 44 const val TYPE_DATETIME_SETTING = 6
37 const val TYPE_RUNNABLE = 7 45 const val TYPE_RUNNABLE = 7
46
47 const val FASTMEM_COMBINED = "fastmem_combined"
48
49 val emptySetting = object : AbstractSetting {
50 override val key: String = ""
51 override val category: Settings.Category = Settings.Category.Ui
52 override val defaultValue: Any = false
53 override fun reset() {}
54 }
55
56 // Extension for putting SettingsItems into a hashmap without repeating yourself
57 fun HashMap<String, SettingsItem>.put(item: SettingsItem) {
58 put(item.setting.key, item)
59 }
60
61 // List of all general
62 val settingsItems = HashMap<String, SettingsItem>().apply {
63 put(
64 SwitchSetting(
65 BooleanSetting.RENDERER_USE_SPEED_LIMIT,
66 R.string.frame_limit_enable,
67 R.string.frame_limit_enable_description
68 )
69 )
70 put(
71 SliderSetting(
72 ShortSetting.RENDERER_SPEED_LIMIT,
73 R.string.frame_limit_slider,
74 R.string.frame_limit_slider_description,
75 1,
76 200,
77 "%"
78 )
79 )
80 put(
81 SingleChoiceSetting(
82 IntSetting.CPU_ACCURACY,
83 R.string.cpu_accuracy,
84 0,
85 R.array.cpuAccuracyNames,
86 R.array.cpuAccuracyValues
87 )
88 )
89 put(
90 SwitchSetting(
91 BooleanSetting.PICTURE_IN_PICTURE,
92 R.string.picture_in_picture,
93 R.string.picture_in_picture_description
94 )
95 )
96 put(
97 SwitchSetting(
98 BooleanSetting.USE_DOCKED_MODE,
99 R.string.use_docked_mode,
100 R.string.use_docked_mode_description
101 )
102 )
103 put(
104 SingleChoiceSetting(
105 IntSetting.REGION_INDEX,
106 R.string.emulated_region,
107 0,
108 R.array.regionNames,
109 R.array.regionValues
110 )
111 )
112 put(
113 SingleChoiceSetting(
114 IntSetting.LANGUAGE_INDEX,
115 R.string.emulated_language,
116 0,
117 R.array.languageNames,
118 R.array.languageValues
119 )
120 )
121 put(
122 SwitchSetting(
123 BooleanSetting.USE_CUSTOM_RTC,
124 R.string.use_custom_rtc,
125 R.string.use_custom_rtc_description
126 )
127 )
128 put(DateTimeSetting(LongSetting.CUSTOM_RTC, R.string.set_custom_rtc, 0))
129 put(
130 SingleChoiceSetting(
131 IntSetting.RENDERER_ACCURACY,
132 R.string.renderer_accuracy,
133 0,
134 R.array.rendererAccuracyNames,
135 R.array.rendererAccuracyValues
136 )
137 )
138 put(
139 SingleChoiceSetting(
140 IntSetting.RENDERER_RESOLUTION,
141 R.string.renderer_resolution,
142 0,
143 R.array.rendererResolutionNames,
144 R.array.rendererResolutionValues
145 )
146 )
147 put(
148 SingleChoiceSetting(
149 IntSetting.RENDERER_VSYNC,
150 R.string.renderer_vsync,
151 0,
152 R.array.rendererVSyncNames,
153 R.array.rendererVSyncValues
154 )
155 )
156 put(
157 SingleChoiceSetting(
158 IntSetting.RENDERER_SCALING_FILTER,
159 R.string.renderer_scaling_filter,
160 0,
161 R.array.rendererScalingFilterNames,
162 R.array.rendererScalingFilterValues
163 )
164 )
165 put(
166 SingleChoiceSetting(
167 IntSetting.RENDERER_ANTI_ALIASING,
168 R.string.renderer_anti_aliasing,
169 0,
170 R.array.rendererAntiAliasingNames,
171 R.array.rendererAntiAliasingValues
172 )
173 )
174 put(
175 SingleChoiceSetting(
176 IntSetting.RENDERER_SCREEN_LAYOUT,
177 R.string.renderer_screen_layout,
178 0,
179 R.array.rendererScreenLayoutNames,
180 R.array.rendererScreenLayoutValues
181 )
182 )
183 put(
184 SingleChoiceSetting(
185 IntSetting.RENDERER_ASPECT_RATIO,
186 R.string.renderer_aspect_ratio,
187 0,
188 R.array.rendererAspectRatioNames,
189 R.array.rendererAspectRatioValues
190 )
191 )
192 put(
193 SwitchSetting(
194 BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE,
195 R.string.use_disk_shader_cache,
196 R.string.use_disk_shader_cache_description
197 )
198 )
199 put(
200 SwitchSetting(
201 BooleanSetting.RENDERER_FORCE_MAX_CLOCK,
202 R.string.renderer_force_max_clock,
203 R.string.renderer_force_max_clock_description
204 )
205 )
206 put(
207 SwitchSetting(
208 BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
209 R.string.renderer_asynchronous_shaders,
210 R.string.renderer_asynchronous_shaders_description
211 )
212 )
213 put(
214 SwitchSetting(
215 BooleanSetting.RENDERER_REACTIVE_FLUSHING,
216 R.string.renderer_reactive_flushing,
217 R.string.renderer_reactive_flushing_description
218 )
219 )
220 put(
221 SingleChoiceSetting(
222 IntSetting.AUDIO_OUTPUT_ENGINE,
223 R.string.audio_output_engine,
224 0,
225 R.array.outputEngineEntries,
226 R.array.outputEngineValues
227 )
228 )
229 put(
230 SliderSetting(
231 ByteSetting.AUDIO_VOLUME,
232 R.string.audio_volume,
233 R.string.audio_volume_description,
234 0,
235 100,
236 "%"
237 )
238 )
239 put(
240 SingleChoiceSetting(
241 IntSetting.RENDERER_BACKEND,
242 R.string.renderer_api,
243 0,
244 R.array.rendererApiNames,
245 R.array.rendererApiValues
246 )
247 )
248 put(
249 SwitchSetting(
250 BooleanSetting.RENDERER_DEBUG,
251 R.string.renderer_debug,
252 R.string.renderer_debug_description
253 )
254 )
255 put(
256 SwitchSetting(
257 BooleanSetting.CPU_DEBUG_MODE,
258 R.string.cpu_debug_mode,
259 R.string.cpu_debug_mode_description
260 )
261 )
262
263 val fastmem = object : AbstractBooleanSetting {
264 override val boolean: Boolean
265 get() =
266 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
267
268 override fun setBoolean(value: Boolean) {
269 BooleanSetting.FASTMEM.setBoolean(value)
270 BooleanSetting.FASTMEM_EXCLUSIVES.setBoolean(value)
271 }
272
273 override val key: String = FASTMEM_COMBINED
274 override val category = Settings.Category.Cpu
275 override val isRuntimeModifiable: Boolean = false
276 override val defaultValue: Boolean = true
277 override fun reset() = setBoolean(defaultValue)
278 }
279 put(SwitchSetting(fastmem, R.string.fastmem, 0))
280 }
38 } 281 }
39} 282}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index 7306ec458..705527a73 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -4,36 +4,27 @@
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 6import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7 8
8class SingleChoiceSetting( 9class SingleChoiceSetting(
9 setting: AbstractIntSetting?, 10 setting: AbstractSetting,
10 titleId: Int, 11 titleId: Int,
11 descriptionId: Int, 12 descriptionId: Int,
12 val choicesId: Int, 13 val choicesId: Int,
13 val valuesId: Int, 14 val valuesId: Int
14 val key: String? = null,
15 val defaultValue: Int? = null
16) : SettingsItem(setting, titleId, descriptionId) { 15) : SettingsItem(setting, titleId, descriptionId) {
17 override val type = TYPE_SINGLE_CHOICE 16 override val type = TYPE_SINGLE_CHOICE
18 17
19 val selectedValue: Int 18 var selectedValue: Int
20 get() = if (setting != null) { 19 get() {
21 val setting = setting as AbstractIntSetting 20 return when (setting) {
22 setting.int 21 is AbstractIntSetting -> setting.int
23 } else { 22 else -> -1
24 defaultValue!! 23 }
24 }
25 set(value) {
26 when (setting) {
27 is AbstractIntSetting -> setting.setInt(value)
28 }
25 } 29 }
26
27 /**
28 * Write a value to the backing int. If that int was previously null,
29 * initializes a new one and returns it, so it can be added to the Hashmap.
30 *
31 * @param selection New value of the int.
32 * @return the existing setting with the new value applied.
33 */
34 fun setSelectedValue(selection: Int): AbstractIntSetting {
35 val intSetting = setting as AbstractIntSetting
36 intSetting.int = selection
37 return intSetting
38 }
39} 30}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
index 92d0167ae..c3b5df02c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SliderSetting.kt
@@ -3,60 +3,39 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import kotlin.math.roundToInt 6import org.yuzu.yuzu_emu.features.settings.model.AbstractByteSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting 7import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 8import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 9import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
10import org.yuzu.yuzu_emu.utils.Log 10import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
11import kotlin.math.roundToInt
11 12
12class SliderSetting( 13class SliderSetting(
13 setting: AbstractSetting?, 14 setting: AbstractSetting,
14 titleId: Int, 15 titleId: Int,
15 descriptionId: Int, 16 descriptionId: Int,
16 val min: Int, 17 val min: Int,
17 val max: Int, 18 val max: Int,
18 val units: String, 19 val units: String
19 val key: String? = null,
20 val defaultValue: Int? = null
21) : SettingsItem(setting, titleId, descriptionId) { 20) : SettingsItem(setting, titleId, descriptionId) {
22 override val type = TYPE_SLIDER 21 override val type = TYPE_SLIDER
23 22
24 val selectedValue: Int 23 var selectedValue: Int
25 get() { 24 get() {
26 val setting = setting ?: return defaultValue!!
27 return when (setting) { 25 return when (setting) {
26 is AbstractByteSetting -> setting.byte.toInt()
27 is AbstractShortSetting -> setting.short.toInt()
28 is AbstractIntSetting -> setting.int 28 is AbstractIntSetting -> setting.int
29 is AbstractFloatSetting -> setting.float.roundToInt() 29 is AbstractFloatSetting -> setting.float.roundToInt()
30 else -> { 30 else -> -1
31 Log.error("[SliderSetting] Error casting setting type.") 31 }
32 -1 32 }
33 } 33 set(value) {
34 when (setting) {
35 is AbstractByteSetting -> setting.setByte(value.toByte())
36 is AbstractShortSetting -> setting.setShort(value.toShort())
37 is AbstractIntSetting -> setting.setInt(value)
38 is AbstractFloatSetting -> setting.setFloat(value.toFloat())
34 } 39 }
35 } 40 }
36
37 /**
38 * Write a value to the backing int. If that int was previously null,
39 * initializes a new one and returns it, so it can be added to the Hashmap.
40 *
41 * @param selection New value of the int.
42 * @return the existing setting with the new value applied.
43 */
44 fun setSelectedValue(selection: Int): AbstractIntSetting {
45 val intSetting = setting as AbstractIntSetting
46 intSetting.int = selection
47 return intSetting
48 }
49
50 /**
51 * Write a value to the backing float. If that float was previously null,
52 * initializes a new one and returns it, so it can be added to the Hashmap.
53 *
54 * @param selection New value of the float.
55 * @return the existing setting with the new value applied.
56 */
57 fun setSelectedValue(selection: Float): AbstractFloatSetting {
58 val floatSetting = setting as AbstractFloatSetting
59 floatSetting.float = selection
60 return floatSetting
61 }
62} 41}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
index 3b6731dcd..871dab4f3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -3,57 +3,31 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting 6import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
8 7
9class StringSingleChoiceSetting( 8class StringSingleChoiceSetting(
10 setting: AbstractSetting?, 9 private val stringSetting: AbstractStringSetting,
11 titleId: Int, 10 titleId: Int,
12 descriptionId: Int, 11 descriptionId: Int,
13 val choices: Array<String>, 12 val choices: Array<String>,
14 val values: Array<String>?, 13 val values: Array<String>
15 val key: String? = null, 14) : SettingsItem(stringSetting, titleId, descriptionId) {
16 private val defaultValue: String? = null
17) : SettingsItem(setting, titleId, descriptionId) {
18 override val type = TYPE_STRING_SINGLE_CHOICE 15 override val type = TYPE_STRING_SINGLE_CHOICE
19 16
20 fun getValueAt(index: Int): String? { 17 fun getValueAt(index: Int): String =
21 if (values == null) return null 18 if (index >= 0 && index < values.size) values[index] else ""
22 return if (index >= 0 && index < values.size) { 19
23 values[index] 20 var selectedValue: String
24 } else { 21 get() = stringSetting.string
25 "" 22 set(value) = stringSetting.setString(value)
26 }
27 }
28 23
29 val selectedValue: String
30 get() = if (setting != null) {
31 val setting = setting as AbstractStringSetting
32 setting.string
33 } else {
34 defaultValue!!
35 }
36 val selectValueIndex: Int 24 val selectValueIndex: Int
37 get() { 25 get() {
38 val selectedValue = selectedValue 26 for (i in values.indices) {
39 for (i in values!!.indices) {
40 if (values[i] == selectedValue) { 27 if (values[i] == selectedValue) {
41 return i 28 return i
42 } 29 }
43 } 30 }
44 return -1 31 return -1
45 } 32 }
46
47 /**
48 * Write a value to the backing int. If that int was previously null,
49 * initializes a new one and returns it, so it can be added to the Hashmap.
50 *
51 * @param selection New value of the int.
52 * @return the existing setting with the new value applied.
53 */
54 fun setSelectedValue(selection: String): AbstractStringSetting {
55 val stringSetting = setting as AbstractStringSetting
56 stringSetting.string = selection
57 return stringSetting
58 }
59} 33}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index 8a9d13a92..91c273964 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -7,6 +7,6 @@ class SubmenuSetting(
7 titleId: Int, 7 titleId: Int,
8 descriptionId: Int, 8 descriptionId: Int,
9 val menuKey: String 9 val menuKey: String
10) : SettingsItem(null, titleId, descriptionId) { 10) : SettingsItem(emptySetting, titleId, descriptionId) {
11 override val type = TYPE_SUBMENU 11 override val type = TYPE_SUBMENU
12} 12}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
index 90b198718..416967e64 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SwitchSetting.kt
@@ -10,53 +10,22 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
10class SwitchSetting( 10class SwitchSetting(
11 setting: AbstractSetting, 11 setting: AbstractSetting,
12 titleId: Int, 12 titleId: Int,
13 descriptionId: Int, 13 descriptionId: Int
14 val key: String? = null,
15 val defaultValue: Any? = null
16) : SettingsItem(setting, titleId, descriptionId) { 14) : SettingsItem(setting, titleId, descriptionId) {
17 override val type = TYPE_SWITCH 15 override val type = TYPE_SWITCH
18 16
19 val isChecked: Boolean 17 var checked: Boolean
20 get() { 18 get() {
21 if (setting == null) { 19 return when (setting) {
22 return defaultValue as Boolean 20 is AbstractIntSetting -> setting.int == 1
21 is AbstractBooleanSetting -> setting.boolean
22 else -> false
23 } 23 }
24
25 // Try integer setting
26 try {
27 val setting = setting as AbstractIntSetting
28 return setting.int == 1
29 } catch (_: ClassCastException) {
30 }
31
32 // Try boolean setting
33 try {
34 val setting = setting as AbstractBooleanSetting
35 return setting.boolean
36 } catch (_: ClassCastException) {
37 }
38 return defaultValue as Boolean
39 } 24 }
40 25 set(value) {
41 /** 26 when (setting) {
42 * Write a value to the backing boolean. If that boolean was previously null, 27 is AbstractIntSetting -> setting.setInt(if (value) 1 else 0)
43 * initializes a new one and returns it, so it can be added to the Hashmap. 28 is AbstractBooleanSetting -> setting.setBoolean(value)
44 * 29 }
45 * @param checked Pretty self explanatory.
46 * @return the existing setting with the new value applied.
47 */
48 fun setChecked(checked: Boolean): AbstractSetting {
49 // Try integer setting
50 try {
51 val setting = setting as AbstractIntSetting
52 setting.int = if (checked) 1 else 0
53 return setting
54 } catch (_: ClassCastException) {
55 } 30 }
56
57 // Try boolean setting
58 val setting = setting as AbstractBooleanSetting
59 setting.boolean = checked
60 return setting
61 }
62} 31}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index a5af5a7ae..908c01265 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -3,42 +3,34 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context
7import android.content.Intent
8import android.os.Bundle 6import android.os.Bundle
9import android.view.Menu
10import android.view.View 7import android.view.View
11import android.view.ViewGroup.MarginLayoutParams 8import android.view.ViewGroup.MarginLayoutParams
12import android.widget.Toast 9import android.widget.Toast
13import androidx.activity.OnBackPressedCallback 10import androidx.activity.OnBackPressedCallback
14import androidx.activity.result.ActivityResultLauncher
15import androidx.activity.viewModels 11import androidx.activity.viewModels
16import androidx.appcompat.app.AppCompatActivity 12import androidx.appcompat.app.AppCompatActivity
17import androidx.core.view.ViewCompat 13import androidx.core.view.ViewCompat
18import androidx.core.view.WindowCompat 14import androidx.core.view.WindowCompat
19import androidx.core.view.WindowInsetsCompat 15import androidx.core.view.WindowInsetsCompat
20import androidx.core.view.updatePadding 16import androidx.navigation.fragment.NavHostFragment
17import androidx.navigation.navArgs
21import com.google.android.material.color.MaterialColors 18import com.google.android.material.color.MaterialColors
22import java.io.IOException 19import java.io.IOException
23import org.yuzu.yuzu_emu.R 20import org.yuzu.yuzu_emu.R
24import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 21import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
25import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
26import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
27import org.yuzu.yuzu_emu.features.settings.model.IntSetting
28import org.yuzu.yuzu_emu.features.settings.model.Settings 22import org.yuzu.yuzu_emu.features.settings.model.Settings
29import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
30import org.yuzu.yuzu_emu.features.settings.model.StringSetting
31import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 23import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
24import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
25import org.yuzu.yuzu_emu.model.SettingsViewModel
32import org.yuzu.yuzu_emu.utils.* 26import org.yuzu.yuzu_emu.utils.*
33 27
34class SettingsActivity : AppCompatActivity(), SettingsActivityView { 28class SettingsActivity : AppCompatActivity() {
35 private val presenter = SettingsActivityPresenter(this)
36
37 private lateinit var binding: ActivitySettingsBinding 29 private lateinit var binding: ActivitySettingsBinding
38 30
39 private val settingsViewModel: SettingsViewModel by viewModels() 31 private val args by navArgs<SettingsActivityArgs>()
40 32
41 override val settings: Settings get() = settingsViewModel.settings 33 private val settingsViewModel: SettingsViewModel by viewModels()
42 34
43 override fun onCreate(savedInstanceState: Bundle?) { 35 override fun onCreate(savedInstanceState: Bundle?) {
44 ThemeHelper.setTheme(this) 36 ThemeHelper.setTheme(this)
@@ -48,16 +40,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
48 binding = ActivitySettingsBinding.inflate(layoutInflater) 40 binding = ActivitySettingsBinding.inflate(layoutInflater)
49 setContentView(binding.root) 41 setContentView(binding.root)
50 42
51 WindowCompat.setDecorFitsSystemWindows(window, false) 43 settingsViewModel.game = args.game
52 44
53 val launcher = intent 45 val navHostFragment =
54 val gameID = launcher.getStringExtra(ARG_GAME_ID) 46 supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
55 val menuTag = launcher.getStringExtra(ARG_MENU_TAG) 47 navHostFragment.navController.setGraph(R.navigation.settings_navigation, intent.extras)
56 presenter.onCreate(savedInstanceState, menuTag!!, gameID!!)
57 48
58 // Show "Back" button in the action bar for navigation 49 WindowCompat.setDecorFitsSystemWindows(window, false)
59 setSupportActionBar(binding.toolbarSettings) 50
60 supportActionBar!!.setDisplayHomeAsUpEnabled(true) 51 if (savedInstanceState != null) {
52 settingsViewModel.shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
53 }
61 54
62 if (InsetsHelper.getSystemGestureType(applicationContext) != 55 if (InsetsHelper.getSystemGestureType(applicationContext) !=
63 InsetsHelper.GESTURE_NAVIGATION 56 InsetsHelper.GESTURE_NAVIGATION
@@ -73,6 +66,28 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
73 ) 66 )
74 } 67 }
75 68
69 settingsViewModel.shouldRecreate.observe(this) {
70 if (it) {
71 settingsViewModel.setShouldRecreate(false)
72 recreate()
73 }
74 }
75 settingsViewModel.shouldNavigateBack.observe(this) {
76 if (it) {
77 settingsViewModel.setShouldNavigateBack(false)
78 navigateBack()
79 }
80 }
81 settingsViewModel.shouldShowResetSettingsDialog.observe(this) {
82 if (it) {
83 settingsViewModel.setShouldShowResetSettingsDialog(false)
84 ResetSettingsDialogFragment().show(
85 supportFragmentManager,
86 ResetSettingsDialogFragment.TAG
87 )
88 }
89 }
90
76 onBackPressedDispatcher.addCallback( 91 onBackPressedDispatcher.addCallback(
77 this, 92 this,
78 object : OnBackPressedCallback(true) { 93 object : OnBackPressedCallback(true) {
@@ -83,34 +98,28 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
83 setInsets() 98 setInsets()
84 } 99 }
85 100
86 override fun onSupportNavigateUp(): Boolean { 101 fun navigateBack() {
87 navigateBack() 102 val navHostFragment =
88 return true 103 supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
89 } 104 if (navHostFragment.childFragmentManager.backStackEntryCount > 0) {
90 105 navHostFragment.navController.popBackStack()
91 private fun navigateBack() {
92 if (supportFragmentManager.backStackEntryCount > 0) {
93 supportFragmentManager.popBackStack()
94 } else { 106 } else {
95 finish() 107 finish()
96 } 108 }
97 } 109 }
98 110
99 override fun onCreateOptionsMenu(menu: Menu): Boolean {
100 val inflater = menuInflater
101 inflater.inflate(R.menu.menu_settings, menu)
102 return true
103 }
104
105 override fun onSaveInstanceState(outState: Bundle) { 111 override fun onSaveInstanceState(outState: Bundle) {
106 // Critical: If super method is not called, rotations will be busted. 112 // Critical: If super method is not called, rotations will be busted.
107 super.onSaveInstanceState(outState) 113 super.onSaveInstanceState(outState)
108 presenter.saveState(outState) 114 outState.putBoolean(KEY_SHOULD_SAVE, settingsViewModel.shouldSave)
109 } 115 }
110 116
111 override fun onStart() { 117 override fun onStart() {
112 super.onStart() 118 super.onStart()
113 presenter.onStart() 119 // TODO: Load custom settings contextually
120 if (!DirectoryInitialization.areDirectoriesReady) {
121 DirectoryInitialization.start()
122 }
114 } 123 }
115 124
116 /** 125 /**
@@ -120,143 +129,51 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
120 */ 129 */
121 override fun onStop() { 130 override fun onStop() {
122 super.onStop() 131 super.onStop()
123 presenter.onStop(isFinishing) 132 if (isFinishing && settingsViewModel.shouldSave) {
124 } 133 Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
125 134 Settings.saveSettings()
126 override fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) {
127 if (!addToStack && settingsFragment != null) {
128 return
129 }
130
131 val transaction = supportFragmentManager.beginTransaction()
132 if (addToStack) {
133 if (areSystemAnimationsEnabled()) {
134 transaction.setCustomAnimations(
135 R.anim.anim_settings_fragment_in,
136 R.anim.anim_settings_fragment_out,
137 0,
138 R.anim.anim_pop_settings_fragment_out
139 )
140 }
141 transaction.addToBackStack(null)
142 } 135 }
143 transaction.replace(
144 R.id.frame_content,
145 SettingsFragment.newInstance(menuTag, gameId),
146 FRAGMENT_TAG
147 )
148 transaction.commit()
149 } 136 }
150 137
151 private fun areSystemAnimationsEnabled(): Boolean { 138 override fun onDestroy() {
152 val duration = android.provider.Settings.Global.getFloat( 139 settingsViewModel.clear()
153 contentResolver, 140 super.onDestroy()
154 android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
155 1f
156 )
157 val transition = android.provider.Settings.Global.getFloat(
158 contentResolver,
159 android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
160 1f
161 )
162 return duration != 0f && transition != 0f
163 }
164
165 override fun onSettingsFileLoaded() {
166 val fragment: SettingsFragmentView? = settingsFragment
167 fragment?.loadSettingsList()
168 }
169
170 override fun onSettingsFileNotFound() {
171 val fragment: SettingsFragmentView? = settingsFragment
172 fragment?.loadSettingsList()
173 }
174
175 override fun showToastMessage(message: String, is_long: Boolean) {
176 Toast.makeText(
177 this,
178 message,
179 if (is_long) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
180 ).show()
181 }
182
183 override fun onSettingChanged() {
184 presenter.onSettingChanged()
185 } 141 }
186 142
187 fun onSettingsReset() { 143 fun onSettingsReset() {
188 // Prevents saving to a non-existent settings file 144 // Prevents saving to a non-existent settings file
189 presenter.onSettingsReset() 145 settingsViewModel.shouldSave = false
190
191 // Reset the static memory representation of each setting
192 BooleanSetting.clear()
193 FloatSetting.clear()
194 IntSetting.clear()
195 StringSetting.clear()
196 146
197 // Delete settings file because the user may have changed values that do not exist in the UI 147 // Delete settings file because the user may have changed values that do not exist in the UI
198 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) 148 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
199 if (!settingsFile.delete()) { 149 if (!settingsFile.delete()) {
200 throw IOException("Failed to delete $settingsFile") 150 throw IOException("Failed to delete $settingsFile")
201 } 151 }
152 Settings.settingsList.forEach { it.reset() }
202 153
203 showToastMessage(getString(R.string.settings_reset), true) 154 Toast.makeText(
155 applicationContext,
156 getString(R.string.settings_reset),
157 Toast.LENGTH_LONG
158 ).show()
204 finish() 159 finish()
205 } 160 }
206 161
207 fun setToolbarTitle(title: String) {
208 binding.toolbarSettingsLayout.title = title
209 }
210
211 private val settingsFragment: SettingsFragment?
212 get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
213
214 private fun setInsets() { 162 private fun setInsets() {
215 ViewCompat.setOnApplyWindowInsetsListener( 163 ViewCompat.setOnApplyWindowInsetsListener(
216 binding.frameContent 164 binding.navigationBarShade
217 ) { view: View, windowInsets: WindowInsetsCompat -> 165 ) { view: View, windowInsets: WindowInsetsCompat ->
218 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 166 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
219 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
220 view.updatePadding(
221 left = barInsets.left + cutoutInsets.left,
222 right = barInsets.right + cutoutInsets.right
223 )
224 167
225 val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams 168 val mlpShade = view.layoutParams as MarginLayoutParams
226 mlpAppBar.leftMargin = barInsets.left + cutoutInsets.left
227 mlpAppBar.rightMargin = barInsets.right + cutoutInsets.right
228 binding.appbarSettings.layoutParams = mlpAppBar
229
230 val mlpShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
231 mlpShade.height = barInsets.bottom 169 mlpShade.height = barInsets.bottom
232 binding.navigationBarShade.layoutParams = mlpShade 170 view.layoutParams = mlpShade
233 171
234 windowInsets 172 windowInsets
235 } 173 }
236 } 174 }
237 175
238 companion object { 176 companion object {
239 private const val ARG_MENU_TAG = "menu_tag" 177 private const val KEY_SHOULD_SAVE = "should_save"
240 private const val ARG_GAME_ID = "game_id"
241 private const val FRAGMENT_TAG = "settings"
242
243 fun launch(context: Context, menuTag: String?, gameId: String?) {
244 val settings = Intent(context, SettingsActivity::class.java)
245 settings.putExtra(ARG_MENU_TAG, menuTag)
246 settings.putExtra(ARG_GAME_ID, gameId)
247 context.startActivity(settings)
248 }
249
250 fun launch(
251 context: Context,
252 launcher: ActivityResultLauncher<Intent>,
253 menuTag: String?,
254 gameId: String?
255 ) {
256 val settings = Intent(context, SettingsActivity::class.java)
257 settings.putExtra(ARG_MENU_TAG, menuTag)
258 settings.putExtra(ARG_GAME_ID, gameId)
259 launcher.launch(settings)
260 }
261 } 178 }
262} 179}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
deleted file mode 100644
index 93e677b21..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityPresenter.kt
+++ /dev/null
@@ -1,90 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui
5
6import android.content.Context
7import android.os.Bundle
8import android.text.TextUtils
9import java.io.File
10import org.yuzu.yuzu_emu.NativeLibrary
11import org.yuzu.yuzu_emu.features.settings.model.Settings
12import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
13import org.yuzu.yuzu_emu.utils.DirectoryInitialization
14import org.yuzu.yuzu_emu.utils.Log
15
16class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
17 val settings: Settings get() = activityView.settings
18
19 private var shouldSave = false
20 private lateinit var menuTag: String
21 private lateinit var gameId: String
22
23 fun onCreate(savedInstanceState: Bundle?, menuTag: String, gameId: String) {
24 this.menuTag = menuTag
25 this.gameId = gameId
26 if (savedInstanceState != null) {
27 shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
28 }
29 }
30
31 fun onStart() {
32 prepareDirectoriesIfNeeded()
33 }
34
35 private fun loadSettingsUI() {
36 if (!settings.isLoaded) {
37 if (!TextUtils.isEmpty(gameId)) {
38 settings.loadSettings(gameId, activityView)
39 } else {
40 settings.loadSettings(activityView)
41 }
42 }
43 activityView.showSettingsFragment(menuTag, false, gameId)
44 activityView.onSettingsFileLoaded()
45 }
46
47 private fun prepareDirectoriesIfNeeded() {
48 val configFile =
49 File(
50 "${DirectoryInitialization.userDirectory}/config/" +
51 "${SettingsFile.FILE_NAME_CONFIG}.ini"
52 )
53 if (!configFile.exists()) {
54 Log.error(
55 "${DirectoryInitialization.userDirectory}/config/" +
56 "${SettingsFile.FILE_NAME_CONFIG}.ini"
57 )
58 Log.error("yuzu config file could not be found!")
59 }
60
61 if (!DirectoryInitialization.areDirectoriesReady) {
62 DirectoryInitialization.start(activityView as Context)
63 }
64 loadSettingsUI()
65 }
66
67 fun onStop(finishing: Boolean) {
68 if (finishing && shouldSave) {
69 Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
70 settings.saveSettings(activityView)
71 }
72 NativeLibrary.reloadSettings()
73 }
74
75 fun onSettingChanged() {
76 shouldSave = true
77 }
78
79 fun onSettingsReset() {
80 shouldSave = false
81 }
82
83 fun saveState(outState: Bundle) {
84 outState.putBoolean(KEY_SHOULD_SAVE, shouldSave)
85 }
86
87 companion object {
88 private const val KEY_SHOULD_SAVE = "should_save"
89 }
90}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
deleted file mode 100644
index c186fc388..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivityView.kt
+++ /dev/null
@@ -1,57 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui
5
6import org.yuzu.yuzu_emu.features.settings.model.Settings
7
8/**
9 * Abstraction for the Activity that manages SettingsFragments.
10 */
11interface SettingsActivityView {
12 /**
13 * Show a new SettingsFragment.
14 *
15 * @param menuTag Identifier for the settings group that should be displayed.
16 * @param addToStack Whether or not this fragment should replace a previous one.
17 */
18 fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String)
19
20 /**
21 * Called by a contained Fragment to get access to the Setting HashMap
22 * loaded from disk, so that each Fragment doesn't need to perform its own
23 * read operation.
24 *
25 * @return A HashMap of Settings.
26 */
27 val settings: Settings
28
29 /**
30 * Called when a load operation completes.
31 */
32 fun onSettingsFileLoaded()
33
34 /**
35 * Called when a load operation fails.
36 */
37 fun onSettingsFileNotFound()
38
39 /**
40 * Display a popup text message on screen.
41 *
42 * @param message The contents of the onscreen message.
43 * @param is_long Whether this should be a long Toast or short one.
44 */
45 fun showToastMessage(message: String, is_long: Boolean)
46
47 /**
48 * End the activity.
49 */
50 fun finish()
51
52 /**
53 * Called by a containing Fragment to tell the Activity that a setting was changed;
54 * unless this has been called, the Activity will not save to disk.
55 */
56 fun onSettingChanged()
57}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 9711e2c51..a7a029fc1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -4,51 +4,54 @@
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context 6import android.content.Context
7import android.content.DialogInterface
8import android.icu.util.Calendar 7import android.icu.util.Calendar
9import android.icu.util.TimeZone 8import android.icu.util.TimeZone
10import android.text.format.DateFormat 9import android.text.format.DateFormat
11import android.view.LayoutInflater 10import android.view.LayoutInflater
12import android.view.ViewGroup 11import android.view.ViewGroup
13import android.widget.TextView 12import androidx.fragment.app.Fragment
14import androidx.appcompat.app.AlertDialog 13import androidx.lifecycle.Lifecycle
15import androidx.appcompat.app.AppCompatActivity 14import androidx.lifecycle.ViewModelProvider
16import androidx.recyclerview.widget.RecyclerView 15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
17import androidx.navigation.findNavController
18import androidx.recyclerview.widget.AsyncDifferConfig
19import androidx.recyclerview.widget.DiffUtil
20import androidx.recyclerview.widget.ListAdapter
17import com.google.android.material.datepicker.MaterialDatePicker 21import com.google.android.material.datepicker.MaterialDatePicker
18import com.google.android.material.dialog.MaterialAlertDialogBuilder
19import com.google.android.material.slider.Slider
20import com.google.android.material.timepicker.MaterialTimePicker 22import com.google.android.material.timepicker.MaterialTimePicker
21import com.google.android.material.timepicker.TimeFormat 23import com.google.android.material.timepicker.TimeFormat
24import kotlinx.coroutines.launch
22import org.yuzu.yuzu_emu.R 25import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.DialogSliderBinding 26import org.yuzu.yuzu_emu.SettingsNavigationDirections
24import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 27import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
25import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding 28import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
26import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding 29import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
27import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
28import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
29import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
30import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
31import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
32import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
33import org.yuzu.yuzu_emu.features.settings.model.view.* 30import org.yuzu.yuzu_emu.features.settings.model.view.*
34import org.yuzu.yuzu_emu.features.settings.ui.viewholder.* 31import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
32import org.yuzu.yuzu_emu.fragments.SettingsDialogFragment
33import org.yuzu.yuzu_emu.model.SettingsViewModel
35 34
36class SettingsAdapter( 35class SettingsAdapter(
37 private val fragmentView: SettingsFragmentView, 36 private val fragment: Fragment,
38 private val context: Context 37 private val context: Context
39) : RecyclerView.Adapter<SettingViewHolder?>(), DialogInterface.OnClickListener { 38) : ListAdapter<SettingsItem, SettingViewHolder>(
40 private var settings: ArrayList<SettingsItem>? = null 39 AsyncDifferConfig.Builder(DiffCallback()).build()
41 private var clickedItem: SettingsItem? = null 40) {
42 private var clickedPosition: Int 41 private val settingsViewModel: SettingsViewModel
43 private var dialog: AlertDialog? = null 42 get() = ViewModelProvider(fragment.requireActivity())[SettingsViewModel::class.java]
44 private var sliderProgress = 0
45 private var textSliderValue: TextView? = null
46
47 private var defaultCancelListener =
48 DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() }
49 43
50 init { 44 init {
51 clickedPosition = -1 45 fragment.viewLifecycleOwner.lifecycleScope.launch {
46 fragment.repeatOnLifecycle(Lifecycle.State.STARTED) {
47 settingsViewModel.adapterItemChanged.collect {
48 if (it != -1) {
49 notifyItemChanged(it)
50 settingsViewModel.setAdapterItemChanged(-1)
51 }
52 }
53 }
54 }
52 } 55 }
53 56
54 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder { 57 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder {
@@ -90,67 +93,41 @@ class SettingsAdapter(
90 } 93 }
91 94
92 override fun onBindViewHolder(holder: SettingViewHolder, position: Int) { 95 override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
93 holder.bind(getItem(position)) 96 holder.bind(currentList[position])
94 } 97 }
95 98
96 private fun getItem(position: Int): SettingsItem { 99 override fun getItemCount(): Int = currentList.size
97 return settings!![position]
98 }
99
100 override fun getItemCount(): Int {
101 return if (settings != null) {
102 settings!!.size
103 } else {
104 0
105 }
106 }
107 100
108 override fun getItemViewType(position: Int): Int { 101 override fun getItemViewType(position: Int): Int {
109 return getItem(position).type 102 return currentList[position].type
110 }
111
112 fun setSettingsList(settings: ArrayList<SettingsItem>?) {
113 this.settings = settings
114 notifyDataSetChanged()
115 }
116
117 fun onBooleanClick(item: SwitchSetting, position: Int, checked: Boolean) {
118 val setting = item.setChecked(checked)
119 fragmentView.putSetting(setting)
120 fragmentView.onSettingChanged()
121 } 103 }
122 104
123 private fun onSingleChoiceClick(item: SingleChoiceSetting) { 105 fun onBooleanClick(item: SwitchSetting, checked: Boolean) {
124 clickedItem = item 106 item.checked = checked
125 val value = getSelectionForSingleChoiceValue(item) 107 settingsViewModel.setShouldReloadSettingsList(true)
126 dialog = MaterialAlertDialogBuilder(context) 108 settingsViewModel.shouldSave = true
127 .setTitle(item.nameId)
128 .setSingleChoiceItems(item.choicesId, value, this)
129 .show()
130 } 109 }
131 110
132 fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) { 111 fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) {
133 clickedPosition = position 112 SettingsDialogFragment.newInstance(
134 onSingleChoiceClick(item) 113 settingsViewModel,
135 } 114 item,
136 115 SettingsItem.TYPE_SINGLE_CHOICE,
137 private fun onStringSingleChoiceClick(item: StringSingleChoiceSetting) { 116 position
138 clickedItem = item 117 ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
139 dialog = MaterialAlertDialogBuilder(context)
140 .setTitle(item.nameId)
141 .setSingleChoiceItems(item.choices, item.selectValueIndex, this)
142 .show()
143 } 118 }
144 119
145 fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) { 120 fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
146 clickedPosition = position 121 SettingsDialogFragment.newInstance(
147 onStringSingleChoiceClick(item) 122 settingsViewModel,
123 item,
124 SettingsItem.TYPE_STRING_SINGLE_CHOICE,
125 position
126 ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
148 } 127 }
149 128
150 fun onDateTimeClick(item: DateTimeSetting, position: Int) { 129 fun onDateTimeClick(item: DateTimeSetting, position: Int) {
151 clickedItem = item 130 val storedTime = item.value * 1000
152 clickedPosition = position
153 val storedTime = java.lang.Long.decode(item.value) * 1000
154 131
155 // Helper to extract hour and minute from epoch time 132 // Helper to extract hour and minute from epoch time
156 val calendar: Calendar = Calendar.getInstance() 133 val calendar: Calendar = Calendar.getInstance()
@@ -158,7 +135,7 @@ class SettingsAdapter(
158 calendar.timeZone = TimeZone.getTimeZone("UTC") 135 calendar.timeZone = TimeZone.getTimeZone("UTC")
159 136
160 var timeFormat: Int = TimeFormat.CLOCK_12H 137 var timeFormat: Int = TimeFormat.CLOCK_12H
161 if (DateFormat.is24HourFormat(fragmentView.activityView as AppCompatActivity)) { 138 if (DateFormat.is24HourFormat(context)) {
162 timeFormat = TimeFormat.CLOCK_24H 139 timeFormat = TimeFormat.CLOCK_24H
163 } 140 }
164 141
@@ -175,7 +152,7 @@ class SettingsAdapter(
175 152
176 datePicker.addOnPositiveButtonClickListener { 153 datePicker.addOnPositiveButtonClickListener {
177 timePicker.show( 154 timePicker.show(
178 (fragmentView.activityView as AppCompatActivity).supportFragmentManager, 155 fragment.childFragmentManager,
179 "TimePicker" 156 "TimePicker"
180 ) 157 )
181 } 158 }
@@ -183,160 +160,50 @@ class SettingsAdapter(
183 var epochTime: Long = datePicker.selection!! / 1000 160 var epochTime: Long = datePicker.selection!! / 1000
184 epochTime += timePicker.hour.toLong() * 60 * 60 161 epochTime += timePicker.hour.toLong() * 60 * 60
185 epochTime += timePicker.minute.toLong() * 60 162 epochTime += timePicker.minute.toLong() * 60
186 val rtcString = epochTime.toString() 163 if (item.value != epochTime) {
187 if (item.value != rtcString) { 164 settingsViewModel.shouldSave = true
188 fragmentView.onSettingChanged() 165 notifyItemChanged(position)
166 item.value = epochTime
189 } 167 }
190 notifyItemChanged(clickedPosition)
191 val setting = item.setSelectedValue(rtcString)
192 fragmentView.putSetting(setting)
193 clickedItem = null
194 } 168 }
195 datePicker.show( 169 datePicker.show(
196 (fragmentView.activityView as AppCompatActivity).supportFragmentManager, 170 fragment.childFragmentManager,
197 "DatePicker" 171 "DatePicker"
198 ) 172 )
199 } 173 }
200 174
201 fun onSliderClick(item: SliderSetting, position: Int) { 175 fun onSliderClick(item: SliderSetting, position: Int) {
202 clickedItem = item 176 SettingsDialogFragment.newInstance(
203 clickedPosition = position 177 settingsViewModel,
204 sliderProgress = item.selectedValue 178 item,
205 179 SettingsItem.TYPE_SLIDER,
206 val inflater = LayoutInflater.from(context) 180 position
207 val sliderBinding = DialogSliderBinding.inflate(inflater) 181 ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
208
209 textSliderValue = sliderBinding.textValue
210 textSliderValue!!.text = String.format(
211 context.getString(R.string.value_with_units),
212 sliderProgress.toString(),
213 item.units
214 )
215
216 sliderBinding.slider.apply {
217 valueFrom = item.min.toFloat()
218 valueTo = item.max.toFloat()
219 value = sliderProgress.toFloat()
220 addOnChangeListener { _: Slider, value: Float, _: Boolean ->
221 sliderProgress = value.toInt()
222 textSliderValue!!.text = String.format(
223 context.getString(R.string.value_with_units),
224 sliderProgress.toString(),
225 item.units
226 )
227 }
228 }
229
230 dialog = MaterialAlertDialogBuilder(context)
231 .setTitle(item.nameId)
232 .setView(sliderBinding.root)
233 .setPositiveButton(android.R.string.ok, this)
234 .setNegativeButton(android.R.string.cancel, defaultCancelListener)
235 .show()
236 } 182 }
237 183
238 fun onSubmenuClick(item: SubmenuSetting) { 184 fun onSubmenuClick(item: SubmenuSetting) {
239 fragmentView.loadSubMenu(item.menuKey) 185 val action = SettingsNavigationDirections.actionGlobalSettingsFragment(item.menuKey, null)
186 fragment.view?.findNavController()?.navigate(action)
240 } 187 }
241 188
242 override fun onClick(dialog: DialogInterface, which: Int) { 189 fun onLongClick(item: SettingsItem, position: Int): Boolean {
243 when (clickedItem) { 190 SettingsDialogFragment.newInstance(
244 is SingleChoiceSetting -> { 191 settingsViewModel,
245 val scSetting = clickedItem as SingleChoiceSetting 192 item,
246 val value = getValueForSingleChoiceSelection(scSetting, which) 193 SettingsDialogFragment.TYPE_RESET_SETTING,
247 if (scSetting.selectedValue != value) { 194 position
248 fragmentView.onSettingChanged() 195 ).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
249 }
250
251 // Get the backing Setting, which may be null (if for example it was missing from the file)
252 val setting = scSetting.setSelectedValue(value)
253 fragmentView.putSetting(setting)
254 closeDialog()
255 }
256
257 is StringSingleChoiceSetting -> {
258 val scSetting = clickedItem as StringSingleChoiceSetting
259 val value = scSetting.getValueAt(which)
260 if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
261 val setting = scSetting.setSelectedValue(value!!)
262 fragmentView.putSetting(setting)
263 closeDialog()
264 }
265
266 is SliderSetting -> {
267 val sliderSetting = clickedItem as SliderSetting
268 if (sliderSetting.selectedValue != sliderProgress) {
269 fragmentView.onSettingChanged()
270 }
271 if (sliderSetting.setting is FloatSetting) {
272 val value = sliderProgress.toFloat()
273 val setting = sliderSetting.setSelectedValue(value)
274 fragmentView.putSetting(setting)
275 } else {
276 val setting = sliderSetting.setSelectedValue(sliderProgress)
277 fragmentView.putSetting(setting)
278 }
279 closeDialog()
280 }
281 }
282 clickedItem = null
283 sliderProgress = -1
284 }
285
286 fun onLongClick(setting: AbstractSetting, position: Int): Boolean {
287 MaterialAlertDialogBuilder(context)
288 .setMessage(R.string.reset_setting_confirmation)
289 .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int ->
290 when (setting) {
291 is AbstractBooleanSetting -> setting.boolean = setting.defaultValue as Boolean
292 is AbstractFloatSetting -> setting.float = setting.defaultValue as Float
293 is AbstractIntSetting -> setting.int = setting.defaultValue as Int
294 is AbstractStringSetting -> setting.string = setting.defaultValue as String
295 }
296 notifyItemChanged(position)
297 fragmentView.onSettingChanged()
298 }
299 .setNegativeButton(android.R.string.cancel, null)
300 .show()
301 196
302 return true 197 return true
303 } 198 }
304 199
305 fun closeDialog() { 200 private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
306 if (dialog != null) { 201 override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
307 if (clickedPosition != -1) { 202 return oldItem.setting.key == newItem.setting.key
308 notifyItemChanged(clickedPosition)
309 clickedPosition = -1
310 }
311 dialog!!.dismiss()
312 dialog = null
313 } 203 }
314 }
315 204
316 private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int { 205 override fun areContentsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
317 val valuesId = item.valuesId 206 return oldItem.setting.key == newItem.setting.key
318 return if (valuesId > 0) {
319 val valuesArray = context.resources.getIntArray(valuesId)
320 valuesArray[which]
321 } else {
322 which
323 }
324 }
325
326 private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int {
327 val value = item.selectedValue
328 val valuesId = item.valuesId
329 if (valuesId > 0) {
330 val valuesArray = context.resources.getIntArray(valuesId)
331 for (index in valuesArray.indices) {
332 val current = valuesArray[index]
333 if (current == value) {
334 return index
335 }
336 }
337 } else {
338 return value
339 } 207 }
340 return -1
341 } 208 }
342} 209}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 70a74c4dd..bc319714c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -3,40 +3,43 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context
7import android.os.Bundle 6import android.os.Bundle
8import android.view.LayoutInflater 7import android.view.LayoutInflater
9import android.view.View 8import android.view.View
10import android.view.ViewGroup 9import android.view.ViewGroup
10import android.view.ViewGroup.MarginLayoutParams
11import androidx.core.view.ViewCompat 11import androidx.core.view.ViewCompat
12import androidx.core.view.WindowInsetsCompat 12import androidx.core.view.WindowInsetsCompat
13import androidx.core.view.updatePadding 13import androidx.core.view.updatePadding
14import androidx.fragment.app.Fragment 14import androidx.fragment.app.Fragment
15import androidx.fragment.app.activityViewModels
16import androidx.navigation.findNavController
17import androidx.navigation.fragment.navArgs
15import androidx.recyclerview.widget.LinearLayoutManager 18import androidx.recyclerview.widget.LinearLayoutManager
16import com.google.android.material.divider.MaterialDividerItemDecoration 19import com.google.android.material.divider.MaterialDividerItemDecoration
20import com.google.android.material.transition.MaterialSharedAxis
21import org.yuzu.yuzu_emu.R
17import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding 22import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
18import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting 23import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
19import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 24import org.yuzu.yuzu_emu.model.SettingsViewModel
20 25
21class SettingsFragment : Fragment(), SettingsFragmentView { 26class SettingsFragment : Fragment() {
22 override var activityView: SettingsActivityView? = null 27 private lateinit var presenter: SettingsFragmentPresenter
23
24 private val fragmentPresenter = SettingsFragmentPresenter(this)
25 private var settingsAdapter: SettingsAdapter? = null 28 private var settingsAdapter: SettingsAdapter? = null
26 29
27 private var _binding: FragmentSettingsBinding? = null 30 private var _binding: FragmentSettingsBinding? = null
28 private val binding get() = _binding!! 31 private val binding get() = _binding!!
29 32
30 override fun onAttach(context: Context) { 33 private val args by navArgs<SettingsFragmentArgs>()
31 super.onAttach(context) 34
32 activityView = requireActivity() as SettingsActivityView 35 private val settingsViewModel: SettingsViewModel by activityViewModels()
33 }
34 36
35 override fun onCreate(savedInstanceState: Bundle?) { 37 override fun onCreate(savedInstanceState: Bundle?) {
36 super.onCreate(savedInstanceState) 38 super.onCreate(savedInstanceState)
37 val menuTag = requireArguments().getString(ARGUMENT_MENU_TAG) 39 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
38 val gameId = requireArguments().getString(ARGUMENT_GAME_ID) 40 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
39 fragmentPresenter.onCreate(menuTag!!, gameId!!) 41 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
42 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
40 } 43 }
41 44
42 override fun onCreateView( 45 override fun onCreateView(
@@ -49,7 +52,14 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
49 } 52 }
50 53
51 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 54 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
52 settingsAdapter = SettingsAdapter(this, requireActivity()) 55 settingsAdapter = SettingsAdapter(this, requireContext())
56 presenter = SettingsFragmentPresenter(
57 settingsViewModel,
58 settingsAdapter!!,
59 args.menuTag,
60 args.game?.gameId ?: ""
61 )
62
53 val dividerDecoration = MaterialDividerItemDecoration( 63 val dividerDecoration = MaterialDividerItemDecoration(
54 requireContext(), 64 requireContext(),
55 LinearLayoutManager.VERTICAL 65 LinearLayoutManager.VERTICAL
@@ -57,71 +67,86 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
57 dividerDecoration.isLastItemDecorated = false 67 dividerDecoration.isLastItemDecorated = false
58 binding.listSettings.apply { 68 binding.listSettings.apply {
59 adapter = settingsAdapter 69 adapter = settingsAdapter
60 layoutManager = LinearLayoutManager(activity) 70 layoutManager = LinearLayoutManager(requireContext())
61 addItemDecoration(dividerDecoration) 71 addItemDecoration(dividerDecoration)
62 } 72 }
63 fragmentPresenter.onViewCreated()
64 73
65 setInsets() 74 binding.toolbarSettings.setNavigationOnClickListener {
66 } 75 settingsViewModel.setShouldNavigateBack(true)
76 }
67 77
68 override fun onDetach() { 78 settingsViewModel.toolbarTitle.observe(viewLifecycleOwner) {
69 super.onDetach() 79 if (it.isNotEmpty()) binding.toolbarSettingsLayout.title = it
70 activityView = null
71 if (settingsAdapter != null) {
72 settingsAdapter!!.closeDialog()
73 } 80 }
74 }
75 81
76 override fun showSettingsList(settingsList: ArrayList<SettingsItem>) { 82 settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) {
77 settingsAdapter!!.setSettingsList(settingsList) 83 if (it) {
78 } 84 settingsViewModel.setShouldReloadSettingsList(false)
85 presenter.loadSettingsList()
86 }
87 }
79 88
80 override fun loadSettingsList() { 89 settingsViewModel.isUsingSearch.observe(viewLifecycleOwner) {
81 fragmentPresenter.loadSettingsList() 90 if (it) {
82 } 91 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
92 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
93 } else {
94 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
95 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
96 }
97 }
83 98
84 override fun loadSubMenu(menuKey: String) { 99 if (args.menuTag == SettingsFile.FILE_NAME_CONFIG) {
85 activityView!!.showSettingsFragment( 100 binding.toolbarSettings.inflateMenu(R.menu.menu_settings)
86 menuKey, 101 binding.toolbarSettings.setOnMenuItemClickListener {
87 true, 102 when (it.itemId) {
88 requireArguments().getString(ARGUMENT_GAME_ID)!! 103 R.id.action_search -> {
89 ) 104 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
90 } 105 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
106 view.findNavController()
107 .navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
108 true
109 }
110
111 else -> false
112 }
113 }
114 }
91 115
92 override fun showToastMessage(message: String?, is_long: Boolean) { 116 presenter.onViewCreated()
93 activityView!!.showToastMessage(message!!, is_long)
94 }
95 117
96 override fun putSetting(setting: AbstractSetting) { 118 setInsets()
97 fragmentPresenter.putSetting(setting)
98 } 119 }
99 120
100 override fun onSettingChanged() { 121 override fun onResume() {
101 activityView!!.onSettingChanged() 122 super.onResume()
123 settingsViewModel.setIsUsingSearch(false)
102 } 124 }
103 125
104 private fun setInsets() { 126 private fun setInsets() {
105 ViewCompat.setOnApplyWindowInsetsListener( 127 ViewCompat.setOnApplyWindowInsetsListener(
106 binding.listSettings 128 binding.root
107 ) { view: View, windowInsets: WindowInsetsCompat -> 129 ) { _: View, windowInsets: WindowInsetsCompat ->
108 val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 130 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
109 view.updatePadding(bottom = insets.bottom) 131 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
132
133 val leftInsets = barInsets.left + cutoutInsets.left
134 val rightInsets = barInsets.right + cutoutInsets.right
135
136 val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
137 val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
138 mlpSettingsList.leftMargin = sideMargin + leftInsets
139 mlpSettingsList.rightMargin = sideMargin + rightInsets
140 binding.listSettings.layoutParams = mlpSettingsList
141 binding.listSettings.updatePadding(
142 bottom = barInsets.bottom
143 )
144
145 val mlpAppBar = binding.appbarSettings.layoutParams as MarginLayoutParams
146 mlpAppBar.leftMargin = leftInsets
147 mlpAppBar.rightMargin = rightInsets
148 binding.appbarSettings.layoutParams = mlpAppBar
110 windowInsets 149 windowInsets
111 } 150 }
112 } 151 }
113
114 companion object {
115 private const val ARGUMENT_MENU_TAG = "menu_tag"
116 private const val ARGUMENT_GAME_ID = "game_id"
117
118 fun newInstance(menuTag: String?, gameId: String?): Fragment {
119 val fragment = SettingsFragment()
120 val arguments = Bundle()
121 arguments.putString(ARGUMENT_MENU_TAG, menuTag)
122 arguments.putString(ARGUMENT_GAME_ID, gameId)
123 fragment.arguments = arguments
124 return fragment
125 }
126 }
127} 152}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 59c1d9d54..22a529b1b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -3,63 +3,66 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context
6import android.content.SharedPreferences 7import android.content.SharedPreferences
7import android.os.Build 8import android.os.Build
8import android.text.TextUtils 9import android.text.TextUtils
10import android.widget.Toast
9import androidx.preference.PreferenceManager 11import androidx.preference.PreferenceManager
10import org.yuzu.yuzu_emu.R 12import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 13import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 14import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
13import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting 15import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
14import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
15import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting 16import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
17import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
16import org.yuzu.yuzu_emu.features.settings.model.IntSetting 18import org.yuzu.yuzu_emu.features.settings.model.IntSetting
19import org.yuzu.yuzu_emu.features.settings.model.LongSetting
17import org.yuzu.yuzu_emu.features.settings.model.Settings 20import org.yuzu.yuzu_emu.features.settings.model.Settings
18import org.yuzu.yuzu_emu.features.settings.model.StringSetting 21import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
19import org.yuzu.yuzu_emu.features.settings.model.view.* 22import org.yuzu.yuzu_emu.features.settings.model.view.*
20import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 23import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
21import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment 24import org.yuzu.yuzu_emu.model.SettingsViewModel
22import org.yuzu.yuzu_emu.utils.ThemeHelper 25import org.yuzu.yuzu_emu.utils.NativeConfig
23 26
24class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) { 27class SettingsFragmentPresenter(
25 private var menuTag: String? = null 28 private val settingsViewModel: SettingsViewModel,
26 private lateinit var gameId: String 29 private val adapter: SettingsAdapter,
27 private var settingsList: ArrayList<SettingsItem>? = null 30 private var menuTag: String,
31 private var gameId: String
32) {
33 private var settingsList = ArrayList<SettingsItem>()
28 34
29 private val settingsActivity get() = fragmentView.activityView as SettingsActivity 35 private val preferences: SharedPreferences
30 private val settings get() = fragmentView.activityView!!.settings 36 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
31 37
32 private lateinit var preferences: SharedPreferences 38 private val context: Context get() = YuzuApplication.appContext
33 39
34 fun onCreate(menuTag: String, gameId: String) { 40 // Extension for populating settings list based on paired settings
35 this.gameId = gameId 41 fun ArrayList<SettingsItem>.add(key: String) {
36 this.menuTag = menuTag 42 val item = SettingsItem.settingsItems[key]!!
43 val pairedSettingKey = item.setting.pairedSettingKey
44 if (pairedSettingKey.isNotEmpty()) {
45 val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false)
46 if (!pairedSettingValue) return
47 }
48 add(item)
37 } 49 }
38 50
39 fun onViewCreated() { 51 fun onViewCreated() {
40 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
41 loadSettingsList() 52 loadSettingsList()
42 } 53 }
43 54
44 fun putSetting(setting: AbstractSetting) {
45 if (setting.section == null || setting.key == null) {
46 return
47 }
48
49 val section = settings.getSection(setting.section!!)!!
50 if (section.getSetting(setting.key!!) == null) {
51 section.putSetting(setting)
52 }
53 }
54
55 fun loadSettingsList() { 55 fun loadSettingsList() {
56 if (!TextUtils.isEmpty(gameId)) { 56 if (!TextUtils.isEmpty(gameId)) {
57 settingsActivity.setToolbarTitle("Game Settings: $gameId") 57 settingsViewModel.setToolbarTitle(
58 context.getString(
59 R.string.advanced_settings_game,
60 gameId
61 )
62 )
58 } 63 }
64
59 val sl = ArrayList<SettingsItem>() 65 val sl = ArrayList<SettingsItem>()
60 if (menuTag == null) {
61 return
62 }
63 when (menuTag) { 66 when (menuTag) {
64 SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl) 67 SettingsFile.FILE_NAME_CONFIG -> addConfigSettings(sl)
65 Settings.SECTION_GENERAL -> addGeneralSettings(sl) 68 Settings.SECTION_GENERAL -> addGeneralSettings(sl)
@@ -69,335 +72,104 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
69 Settings.SECTION_THEME -> addThemeSettings(sl) 72 Settings.SECTION_THEME -> addThemeSettings(sl)
70 Settings.SECTION_DEBUG -> addDebugSettings(sl) 73 Settings.SECTION_DEBUG -> addDebugSettings(sl)
71 else -> { 74 else -> {
72 fragmentView.showToastMessage("Unimplemented menu", false) 75 val context = YuzuApplication.appContext
76 Toast.makeText(
77 context,
78 context.getString(R.string.unimplemented_menu),
79 Toast.LENGTH_SHORT
80 ).show()
73 return 81 return
74 } 82 }
75 } 83 }
76 settingsList = sl 84 settingsList = sl
77 fragmentView.showSettingsList(settingsList!!) 85 adapter.submitList(settingsList)
78 } 86 }
79 87
80 private fun addConfigSettings(sl: ArrayList<SettingsItem>) { 88 private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
81 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.advanced_settings)) 89 settingsViewModel.setToolbarTitle(context.getString(R.string.advanced_settings))
82 sl.apply { 90 sl.apply {
83 add( 91 add(SubmenuSetting(R.string.preferences_general, 0, Settings.SECTION_GENERAL))
84 SubmenuSetting( 92 add(SubmenuSetting(R.string.preferences_system, 0, Settings.SECTION_SYSTEM))
85 R.string.preferences_general, 93 add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.SECTION_RENDERER))
86 0, 94 add(SubmenuSetting(R.string.preferences_audio, 0, Settings.SECTION_AUDIO))
87 Settings.SECTION_GENERAL 95 add(SubmenuSetting(R.string.preferences_debug, 0, Settings.SECTION_DEBUG))
88 ) 96 add(
89 ) 97 RunnableSetting(R.string.reset_to_default, 0, false) {
90 add( 98 settingsViewModel.setShouldShowResetSettingsDialog(true)
91 SubmenuSetting(
92 R.string.preferences_system,
93 0,
94 Settings.SECTION_SYSTEM
95 )
96 )
97 add(
98 SubmenuSetting(
99 R.string.preferences_graphics,
100 0,
101 Settings.SECTION_RENDERER
102 )
103 )
104 add(
105 SubmenuSetting(
106 R.string.preferences_audio,
107 0,
108 Settings.SECTION_AUDIO
109 )
110 )
111 add(
112 SubmenuSetting(
113 R.string.preferences_debug,
114 0,
115 Settings.SECTION_DEBUG
116 )
117 )
118 add(
119 RunnableSetting(
120 R.string.reset_to_default,
121 0,
122 false
123 ) {
124 ResetSettingsDialogFragment().show(
125 settingsActivity.supportFragmentManager,
126 ResetSettingsDialogFragment.TAG
127 )
128 } 99 }
129 ) 100 )
130 } 101 }
131 } 102 }
132 103
133 private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { 104 private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
134 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_general)) 105 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_general))
135 sl.apply { 106 sl.apply {
136 add( 107 add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
137 SwitchSetting( 108 add(ShortSetting.RENDERER_SPEED_LIMIT.key)
138 IntSetting.RENDERER_USE_SPEED_LIMIT, 109 add(IntSetting.CPU_ACCURACY.key)
139 R.string.frame_limit_enable, 110 add(BooleanSetting.PICTURE_IN_PICTURE.key)
140 R.string.frame_limit_enable_description,
141 IntSetting.RENDERER_USE_SPEED_LIMIT.key,
142 IntSetting.RENDERER_USE_SPEED_LIMIT.defaultValue
143 )
144 )
145 add(
146 SliderSetting(
147 IntSetting.RENDERER_SPEED_LIMIT,
148 R.string.frame_limit_slider,
149 R.string.frame_limit_slider_description,
150 1,
151 200,
152 "%",
153 IntSetting.RENDERER_SPEED_LIMIT.key,
154 IntSetting.RENDERER_SPEED_LIMIT.defaultValue
155 )
156 )
157 add(
158 SingleChoiceSetting(
159 IntSetting.CPU_ACCURACY,
160 R.string.cpu_accuracy,
161 0,
162 R.array.cpuAccuracyNames,
163 R.array.cpuAccuracyValues,
164 IntSetting.CPU_ACCURACY.key,
165 IntSetting.CPU_ACCURACY.defaultValue
166 )
167 )
168 add(
169 SwitchSetting(
170 BooleanSetting.PICTURE_IN_PICTURE,
171 R.string.picture_in_picture,
172 R.string.picture_in_picture_description,
173 BooleanSetting.PICTURE_IN_PICTURE.key,
174 BooleanSetting.PICTURE_IN_PICTURE.defaultValue
175 )
176 )
177 } 111 }
178 } 112 }
179 113
180 private fun addSystemSettings(sl: ArrayList<SettingsItem>) { 114 private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
181 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_system)) 115 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_system))
182 sl.apply { 116 sl.apply {
183 add( 117 add(BooleanSetting.USE_DOCKED_MODE.key)
184 SwitchSetting( 118 add(IntSetting.REGION_INDEX.key)
185 IntSetting.USE_DOCKED_MODE, 119 add(IntSetting.LANGUAGE_INDEX.key)
186 R.string.use_docked_mode, 120 add(BooleanSetting.USE_CUSTOM_RTC.key)
187 R.string.use_docked_mode_description, 121 add(LongSetting.CUSTOM_RTC.key)
188 IntSetting.USE_DOCKED_MODE.key,
189 IntSetting.USE_DOCKED_MODE.defaultValue
190 )
191 )
192 add(
193 SingleChoiceSetting(
194 IntSetting.REGION_INDEX,
195 R.string.emulated_region,
196 0,
197 R.array.regionNames,
198 R.array.regionValues,
199 IntSetting.REGION_INDEX.key,
200 IntSetting.REGION_INDEX.defaultValue
201 )
202 )
203 add(
204 SingleChoiceSetting(
205 IntSetting.LANGUAGE_INDEX,
206 R.string.emulated_language,
207 0,
208 R.array.languageNames,
209 R.array.languageValues,
210 IntSetting.LANGUAGE_INDEX.key,
211 IntSetting.LANGUAGE_INDEX.defaultValue
212 )
213 )
214 add(
215 SwitchSetting(
216 BooleanSetting.USE_CUSTOM_RTC,
217 R.string.use_custom_rtc,
218 R.string.use_custom_rtc_description,
219 BooleanSetting.USE_CUSTOM_RTC.key,
220 BooleanSetting.USE_CUSTOM_RTC.defaultValue
221 )
222 )
223 add(
224 DateTimeSetting(
225 StringSetting.CUSTOM_RTC,
226 R.string.set_custom_rtc,
227 0,
228 StringSetting.CUSTOM_RTC.key,
229 StringSetting.CUSTOM_RTC.defaultValue
230 )
231 )
232 } 122 }
233 } 123 }
234 124
235 private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) { 125 private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
236 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics)) 126 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_graphics))
237 sl.apply { 127 sl.apply {
238 add( 128 add(IntSetting.RENDERER_ACCURACY.key)
239 SingleChoiceSetting( 129 add(IntSetting.RENDERER_RESOLUTION.key)
240 IntSetting.RENDERER_ACCURACY, 130 add(IntSetting.RENDERER_VSYNC.key)
241 R.string.renderer_accuracy, 131 add(IntSetting.RENDERER_SCALING_FILTER.key)
242 0, 132 add(IntSetting.RENDERER_ANTI_ALIASING.key)
243 R.array.rendererAccuracyNames, 133 add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
244 R.array.rendererAccuracyValues, 134 add(IntSetting.RENDERER_ASPECT_RATIO.key)
245 IntSetting.RENDERER_ACCURACY.key, 135 add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
246 IntSetting.RENDERER_ACCURACY.defaultValue 136 add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
247 ) 137 add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
248 ) 138 add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
249 add(
250 SingleChoiceSetting(
251 IntSetting.RENDERER_RESOLUTION,
252 R.string.renderer_resolution,
253 0,
254 R.array.rendererResolutionNames,
255 R.array.rendererResolutionValues,
256 IntSetting.RENDERER_RESOLUTION.key,
257 IntSetting.RENDERER_RESOLUTION.defaultValue
258 )
259 )
260 add(
261 SingleChoiceSetting(
262 IntSetting.RENDERER_VSYNC,
263 R.string.renderer_vsync,
264 0,
265 R.array.rendererVSyncNames,
266 R.array.rendererVSyncValues,
267 IntSetting.RENDERER_VSYNC.key,
268 IntSetting.RENDERER_VSYNC.defaultValue
269 )
270 )
271 add(
272 SingleChoiceSetting(
273 IntSetting.RENDERER_SCALING_FILTER,
274 R.string.renderer_scaling_filter,
275 0,
276 R.array.rendererScalingFilterNames,
277 R.array.rendererScalingFilterValues,
278 IntSetting.RENDERER_SCALING_FILTER.key,
279 IntSetting.RENDERER_SCALING_FILTER.defaultValue
280 )
281 )
282 add(
283 SingleChoiceSetting(
284 IntSetting.RENDERER_ANTI_ALIASING,
285 R.string.renderer_anti_aliasing,
286 0,
287 R.array.rendererAntiAliasingNames,
288 R.array.rendererAntiAliasingValues,
289 IntSetting.RENDERER_ANTI_ALIASING.key,
290 IntSetting.RENDERER_ANTI_ALIASING.defaultValue
291 )
292 )
293 add(
294 SingleChoiceSetting(
295 IntSetting.RENDERER_SCREEN_LAYOUT,
296 R.string.renderer_screen_layout,
297 0,
298 R.array.rendererScreenLayoutNames,
299 R.array.rendererScreenLayoutValues,
300 IntSetting.RENDERER_SCREEN_LAYOUT.key,
301 IntSetting.RENDERER_SCREEN_LAYOUT.defaultValue
302 )
303 )
304 add(
305 SingleChoiceSetting(
306 IntSetting.RENDERER_ASPECT_RATIO,
307 R.string.renderer_aspect_ratio,
308 0,
309 R.array.rendererAspectRatioNames,
310 R.array.rendererAspectRatioValues,
311 IntSetting.RENDERER_ASPECT_RATIO.key,
312 IntSetting.RENDERER_ASPECT_RATIO.defaultValue
313 )
314 )
315 add(
316 SwitchSetting(
317 IntSetting.RENDERER_USE_DISK_SHADER_CACHE,
318 R.string.use_disk_shader_cache,
319 R.string.use_disk_shader_cache_description,
320 IntSetting.RENDERER_USE_DISK_SHADER_CACHE.key,
321 IntSetting.RENDERER_USE_DISK_SHADER_CACHE.defaultValue
322 )
323 )
324 add(
325 SwitchSetting(
326 IntSetting.RENDERER_FORCE_MAX_CLOCK,
327 R.string.renderer_force_max_clock,
328 R.string.renderer_force_max_clock_description,
329 IntSetting.RENDERER_FORCE_MAX_CLOCK.key,
330 IntSetting.RENDERER_FORCE_MAX_CLOCK.defaultValue
331 )
332 )
333 add(
334 SwitchSetting(
335 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS,
336 R.string.renderer_asynchronous_shaders,
337 R.string.renderer_asynchronous_shaders_description,
338 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.key,
339 IntSetting.RENDERER_ASYNCHRONOUS_SHADERS.defaultValue
340 )
341 )
342 add(
343 SwitchSetting(
344 IntSetting.RENDERER_REACTIVE_FLUSHING,
345 R.string.renderer_reactive_flushing,
346 R.string.renderer_reactive_flushing_description,
347 IntSetting.RENDERER_REACTIVE_FLUSHING.key,
348 IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue
349 )
350 )
351 } 139 }
352 } 140 }
353 141
354 private fun addAudioSettings(sl: ArrayList<SettingsItem>) { 142 private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
355 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio)) 143 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_audio))
356 sl.apply { 144 sl.apply {
357 add( 145 add(IntSetting.AUDIO_OUTPUT_ENGINE.key)
358 StringSingleChoiceSetting( 146 add(ByteSetting.AUDIO_VOLUME.key)
359 StringSetting.AUDIO_OUTPUT_ENGINE,
360 R.string.audio_output_engine,
361 0,
362 settingsActivity.resources.getStringArray(R.array.outputEngineEntries),
363 settingsActivity.resources.getStringArray(R.array.outputEngineValues),
364 StringSetting.AUDIO_OUTPUT_ENGINE.key,
365 StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue
366 )
367 )
368 add(
369 SliderSetting(
370 IntSetting.AUDIO_VOLUME,
371 R.string.audio_volume,
372 R.string.audio_volume_description,
373 0,
374 100,
375 "%",
376 IntSetting.AUDIO_VOLUME.key,
377 IntSetting.AUDIO_VOLUME.defaultValue
378 )
379 )
380 } 147 }
381 } 148 }
382 149
383 private fun addThemeSettings(sl: ArrayList<SettingsItem>) { 150 private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
384 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_theme)) 151 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_theme))
385 sl.apply { 152 sl.apply {
386 val theme: AbstractIntSetting = object : AbstractIntSetting { 153 val theme: AbstractIntSetting = object : AbstractIntSetting {
387 override var int: Int 154 override val int: Int
388 get() = preferences.getInt(Settings.PREF_THEME, 0) 155 get() = preferences.getInt(Settings.PREF_THEME, 0)
389 set(value) { 156
390 preferences.edit() 157 override fun setInt(value: Int) {
391 .putInt(Settings.PREF_THEME, value) 158 preferences.edit()
392 .apply() 159 .putInt(Settings.PREF_THEME, value)
393 settingsActivity.recreate() 160 .apply()
394 } 161 settingsViewModel.setShouldRecreate(true)
395 override val key: String? = null 162 }
396 override val section: String? = null 163
397 override val isRuntimeEditable: Boolean = false 164 override val key: String = Settings.PREF_THEME
398 override val valueAsString: String 165 override val category = Settings.Category.UiGeneral
399 get() = preferences.getInt(Settings.PREF_THEME, 0).toString() 166 override val isRuntimeModifiable: Boolean = false
400 override val defaultValue: Any = 0 167 override val defaultValue: Int = 0
168 override fun reset() {
169 preferences.edit()
170 .putInt(Settings.PREF_THEME, defaultValue)
171 .apply()
172 }
401 } 173 }
402 174
403 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 175 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -423,20 +195,26 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
423 } 195 }
424 196
425 val themeMode: AbstractIntSetting = object : AbstractIntSetting { 197 val themeMode: AbstractIntSetting = object : AbstractIntSetting {
426 override var int: Int 198 override val int: Int
427 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1) 199 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1)
428 set(value) { 200
429 preferences.edit() 201 override fun setInt(value: Int) {
430 .putInt(Settings.PREF_THEME_MODE, value) 202 preferences.edit()
431 .apply() 203 .putInt(Settings.PREF_THEME_MODE, value)
432 ThemeHelper.setThemeMode(settingsActivity) 204 .apply()
433 } 205 settingsViewModel.setShouldRecreate(true)
434 override val key: String? = null 206 }
435 override val section: String? = null 207
436 override val isRuntimeEditable: Boolean = false 208 override val key: String = Settings.PREF_THEME_MODE
437 override val valueAsString: String 209 override val category = Settings.Category.UiGeneral
438 get() = preferences.getInt(Settings.PREF_THEME_MODE, -1).toString() 210 override val isRuntimeModifiable: Boolean = false
439 override val defaultValue: Any = -1 211 override val defaultValue: Int = -1
212 override fun reset() {
213 preferences.edit()
214 .putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
215 .apply()
216 settingsViewModel.setShouldRecreate(true)
217 }
440 } 218 }
441 219
442 add( 220 add(
@@ -450,21 +228,26 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
450 ) 228 )
451 229
452 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { 230 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
453 override var boolean: Boolean 231 override val boolean: Boolean
454 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
455 set(value) {
456 preferences.edit()
457 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
458 .apply()
459 settingsActivity.recreate()
460 }
461 override val key: String? = null
462 override val section: String? = null
463 override val isRuntimeEditable: Boolean = false
464 override val valueAsString: String
465 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) 232 get() = preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
466 .toString() 233
467 override val defaultValue: Any = false 234 override fun setBoolean(value: Boolean) {
235 preferences.edit()
236 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
237 .apply()
238 settingsViewModel.setShouldRecreate(true)
239 }
240
241 override val key: String = Settings.PREF_BLACK_BACKGROUNDS
242 override val category = Settings.Category.UiGeneral
243 override val isRuntimeModifiable: Boolean = false
244 override val defaultValue: Boolean = false
245 override fun reset() {
246 preferences.edit()
247 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
248 .apply()
249 settingsViewModel.setShouldRecreate(true)
250 }
468 } 251 }
469 252
470 add( 253 add(
@@ -478,62 +261,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
478 } 261 }
479 262
480 private fun addDebugSettings(sl: ArrayList<SettingsItem>) { 263 private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
481 settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) 264 settingsViewModel.setToolbarTitle(context.getString(R.string.preferences_debug))
482 sl.apply { 265 sl.apply {
483 add(HeaderSetting(R.string.gpu)) 266 add(HeaderSetting(R.string.gpu))
484 add( 267 add(IntSetting.RENDERER_BACKEND.key)
485 SingleChoiceSetting( 268 add(BooleanSetting.RENDERER_DEBUG.key)
486 IntSetting.RENDERER_BACKEND,
487 R.string.renderer_api,
488 0,
489 R.array.rendererApiNames,
490 R.array.rendererApiValues,
491 IntSetting.RENDERER_BACKEND.key,
492 IntSetting.RENDERER_BACKEND.defaultValue
493 )
494 )
495 add(
496 SwitchSetting(
497 IntSetting.RENDERER_DEBUG,
498 R.string.renderer_debug,
499 R.string.renderer_debug_description,
500 IntSetting.RENDERER_DEBUG.key,
501 IntSetting.RENDERER_DEBUG.defaultValue
502 )
503 )
504 269
505 add(HeaderSetting(R.string.cpu)) 270 add(HeaderSetting(R.string.cpu))
506 add( 271 add(BooleanSetting.CPU_DEBUG_MODE.key)
507 SwitchSetting( 272 add(SettingsItem.FASTMEM_COMBINED)
508 BooleanSetting.CPU_DEBUG_MODE,
509 R.string.cpu_debug_mode,
510 R.string.cpu_debug_mode_description,
511 BooleanSetting.CPU_DEBUG_MODE.key,
512 BooleanSetting.CPU_DEBUG_MODE.defaultValue
513 )
514 )
515
516 val fastmem = object : AbstractBooleanSetting {
517 override var boolean: Boolean
518 get() =
519 BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
520 set(value) {
521 BooleanSetting.FASTMEM.boolean = value
522 BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value
523 }
524 override val key: String? = null
525 override val section: String = Settings.SECTION_CPU
526 override val isRuntimeEditable: Boolean = false
527 override val valueAsString: String = ""
528 override val defaultValue: Any = true
529 }
530 add(
531 SwitchSetting(
532 fastmem,
533 R.string.fastmem,
534 0
535 )
536 )
537 } 273 }
538 } 274 }
539} 275}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
deleted file mode 100644
index 1ebe35eaa..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentView.kt
+++ /dev/null
@@ -1,58 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.features.settings.ui
5
6import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
7import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
8
9/**
10 * Abstraction for a screen showing a list of settings. Instances of
11 * this type of view will each display a layer of the setting hierarchy.
12 */
13interface SettingsFragmentView {
14 /**
15 * Pass an ArrayList to the View so that it can be displayed on screen.
16 *
17 * @param settingsList The result of converting the HashMap to an ArrayList
18 */
19 fun showSettingsList(settingsList: ArrayList<SettingsItem>)
20
21 /**
22 * Instructs the Fragment to load the settings screen.
23 */
24 fun loadSettingsList()
25
26 /**
27 * @return The Fragment's containing activity.
28 */
29 val activityView: SettingsActivityView?
30
31 /**
32 * Tell the Fragment to tell the containing Activity to show a new
33 * Fragment containing a submenu of settings.
34 *
35 * @param menuKey Identifier for the settings group that should be shown.
36 */
37 fun loadSubMenu(menuKey: String)
38
39 /**
40 * Tell the Fragment to tell the containing activity to display a toast message.
41 *
42 * @param message Text to be shown in the Toast
43 * @param is_long Whether this should be a long Toast or short one.
44 */
45 fun showToastMessage(message: String?, is_long: Boolean)
46
47 /**
48 * Have the fragment add a setting to the HashMap.
49 *
50 * @param setting The (possibly previously missing) new setting.
51 */
52 fun putSetting(setting: AbstractSetting)
53
54 /**
55 * Have the fragment tell the containing Activity that a setting was modified.
56 */
57 fun onSettingChanged()
58}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
index 79572fc06..525f013f8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/DateTimeViewHolder.kt
@@ -29,7 +29,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
29 } 29 }
30 30
31 binding.textSettingValue.visibility = View.VISIBLE 31 binding.textSettingValue.visibility = View.VISIBLE
32 val epochTime = setting.value.toLong() 32 val epochTime = setting.value
33 val instant = Instant.ofEpochMilli(epochTime * 1000) 33 val instant = Instant.ofEpochMilli(epochTime * 1000)
34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC")) 34 val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) 35 val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
@@ -46,7 +46,7 @@ class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
46 46
47 override fun onLongClick(clicked: View): Boolean { 47 override fun onLongClick(clicked: View): Boolean {
48 if (setting.isEditable) { 48 if (setting.isEditable) {
49 return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) 49 return adapter.onLongClick(setting, bindingAdapterPosition)
50 } 50 }
51 return false 51 return false
52 } 52 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
index b42d955aa..80d1b22c1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.kt
@@ -35,7 +35,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
35 } 35 }
36 } 36 }
37 } else if (item is StringSingleChoiceSetting) { 37 } else if (item is StringSingleChoiceSetting) {
38 for (i in item.values!!.indices) { 38 for (i in item.values.indices) {
39 if (item.values[i] == item.selectedValue) { 39 if (item.values[i] == item.selectedValue) {
40 binding.textSettingValue.text = item.choices[i] 40 binding.textSettingValue.text = item.choices[i]
41 break 41 break
@@ -66,7 +66,7 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
66 66
67 override fun onLongClick(clicked: View): Boolean { 67 override fun onLongClick(clicked: View): Boolean {
68 if (setting.isEditable) { 68 if (setting.isEditable) {
69 return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) 69 return adapter.onLongClick(setting, bindingAdapterPosition)
70 } 70 }
71 return false 71 return false
72 } 72 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
index a23b5d109..b83c90100 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SliderViewHolder.kt
@@ -41,7 +41,7 @@ class SliderViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAda
41 41
42 override fun onLongClick(clicked: View): Boolean { 42 override fun onLongClick(clicked: View): Boolean {
43 if (setting.isEditable) { 43 if (setting.isEditable) {
44 return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) 44 return adapter.onLongClick(setting, bindingAdapterPosition)
45 } 45 }
46 return false 46 return false
47 } 47 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
index ef34bf5f4..57fdeaa20 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SwitchSettingViewHolder.kt
@@ -25,10 +25,12 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
25 binding.textSettingDescription.text = "" 25 binding.textSettingDescription.text = ""
26 binding.textSettingDescription.visibility = View.GONE 26 binding.textSettingDescription.visibility = View.GONE
27 } 27 }
28
29 binding.switchWidget.setOnCheckedChangeListener(null)
30 binding.switchWidget.isChecked = setting.checked
28 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean -> 31 binding.switchWidget.setOnCheckedChangeListener { _: CompoundButton, _: Boolean ->
29 adapter.onBooleanClick(item, bindingAdapterPosition, binding.switchWidget.isChecked) 32 adapter.onBooleanClick(item, binding.switchWidget.isChecked)
30 } 33 }
31 binding.switchWidget.isChecked = setting.isChecked
32 34
33 setStyle(setting.isEditable, binding) 35 setStyle(setting.isEditable, binding)
34 } 36 }
@@ -41,7 +43,7 @@ class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter
41 43
42 override fun onLongClick(clicked: View): Boolean { 44 override fun onLongClick(clicked: View): Boolean {
43 if (setting.isEditable) { 45 if (setting.isEditable) {
44 return adapter.onLongClick(setting.setting!!, bindingAdapterPosition) 46 return adapter.onLongClick(setting, bindingAdapterPosition)
45 } 47 }
46 return false 48 return false
47 } 49 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
index 70a52df5d..2b04d666a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.kt
@@ -3,18 +3,15 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.utils 4package org.yuzu.yuzu_emu.features.settings.utils
5 5
6import android.widget.Toast
6import java.io.* 7import java.io.*
7import java.util.*
8import org.ini4j.Wini 8import org.ini4j.Wini
9import org.yuzu.yuzu_emu.NativeLibrary
10import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication 10import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.* 11import org.yuzu.yuzu_emu.features.settings.model.*
13import org.yuzu.yuzu_emu.features.settings.model.Settings.SettingsSectionMap
14import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
15import org.yuzu.yuzu_emu.utils.BiMap
16import org.yuzu.yuzu_emu.utils.DirectoryInitialization 12import org.yuzu.yuzu_emu.utils.DirectoryInitialization
17import org.yuzu.yuzu_emu.utils.Log 13import org.yuzu.yuzu_emu.utils.Log
14import org.yuzu.yuzu_emu.utils.NativeConfig
18 15
19/** 16/**
20 * Contains static methods for interacting with .ini files in which settings are stored. 17 * Contains static methods for interacting with .ini files in which settings are stored.
@@ -22,243 +19,41 @@ import org.yuzu.yuzu_emu.utils.Log
22object SettingsFile { 19object SettingsFile {
23 const val FILE_NAME_CONFIG = "config" 20 const val FILE_NAME_CONFIG = "config"
24 21
25 private var sectionsMap = BiMap<String?, String?>()
26
27 /**
28 * Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves
29 * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
30 * failed.
31 *
32 * @param ini The ini file to load the settings from
33 * @param isCustomGame
34 * @param view The current view.
35 * @return An Observable that emits a HashMap of the file's contents, then completes.
36 */
37 private fun readFile(
38 ini: File?,
39 isCustomGame: Boolean,
40 view: SettingsActivityView? = null
41 ): HashMap<String, SettingSection?> {
42 val sections: HashMap<String, SettingSection?> = SettingsSectionMap()
43 var reader: BufferedReader? = null
44 try {
45 reader = BufferedReader(FileReader(ini))
46 var current: SettingSection? = null
47 var line: String?
48 while (reader.readLine().also { line = it } != null) {
49 if (line!!.startsWith("[") && line!!.endsWith("]")) {
50 current = sectionFromLine(line!!, isCustomGame)
51 sections[current.name] = current
52 } else if (current != null) {
53 val setting = settingFromLine(line!!)
54 if (setting != null) {
55 current.putSetting(setting)
56 }
57 }
58 }
59 } catch (e: FileNotFoundException) {
60 Log.error("[SettingsFile] File not found: " + e.message)
61 view?.onSettingsFileNotFound()
62 } catch (e: IOException) {
63 Log.error("[SettingsFile] Error reading from: " + e.message)
64 view?.onSettingsFileNotFound()
65 } finally {
66 if (reader != null) {
67 try {
68 reader.close()
69 } catch (e: IOException) {
70 Log.error("[SettingsFile] Error closing: " + e.message)
71 }
72 }
73 }
74 return sections
75 }
76
77 fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> {
78 return readFile(getSettingsFile(fileName), false, view)
79 }
80
81 fun readFile(fileName: String): HashMap<String, SettingSection?> =
82 readFile(getSettingsFile(fileName), false)
83
84 /**
85 * Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves
86 * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
87 * failed.
88 *
89 * @param gameId the id of the game to load it's settings.
90 * @param view The current view.
91 */
92 fun readCustomGameSettings(
93 gameId: String,
94 view: SettingsActivityView?
95 ): HashMap<String, SettingSection?> {
96 return readFile(getCustomGameSettingsFile(gameId), true, view)
97 }
98
99 /** 22 /**
100 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error 23 * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error
101 * telling why it failed. 24 * telling why it failed.
102 * 25 *
103 * @param fileName The target filename without a path or extension. 26 * @param fileName The target filename without a path or extension.
104 * @param sections The HashMap containing the Settings we want to serialize.
105 * @param view The current view.
106 */ 27 */
107 fun saveFile( 28 fun saveFile(fileName: String) {
108 fileName: String,
109 sections: TreeMap<String, SettingSection>,
110 view: SettingsActivityView
111 ) {
112 val ini = getSettingsFile(fileName) 29 val ini = getSettingsFile(fileName)
113 try { 30 try {
114 val writer = Wini(ini) 31 val wini = Wini(ini)
115 val keySet: Set<String> = sections.keys 32 for (specificCategory in Settings.Category.values()) {
116 for (key in keySet) { 33 val categoryHeader = NativeConfig.getConfigHeader(specificCategory.ordinal)
117 val section = sections[key] 34 for (setting in Settings.settingsList) {
118 writeSection(writer, section!!) 35 if (setting.key!!.isEmpty()) continue
36
37 val settingCategoryHeader =
38 NativeConfig.getConfigHeader(setting.category.ordinal)
39 val iniSetting: String? = wini.get(categoryHeader, setting.key)
40 if (iniSetting != null || settingCategoryHeader == categoryHeader) {
41 wini.put(settingCategoryHeader, setting.key, setting.valueAsString)
42 }
43 }
119 } 44 }
120 writer.store() 45 wini.store()
121 } catch (e: IOException) { 46 } catch (e: IOException) {
122 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message) 47 Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.message)
123 view.showToastMessage( 48 val context = YuzuApplication.appContext
124 YuzuApplication.appContext 49 Toast.makeText(
125 .getString(R.string.error_saving, fileName, e.message), 50 context,
126 false 51 context.getString(R.string.error_saving, fileName, e.message),
127 ) 52 Toast.LENGTH_SHORT
128 } 53 ).show()
129 }
130
131 fun saveCustomGameSettings(gameId: String?, sections: HashMap<String, SettingSection?>) {
132 val sortedSections: Set<String> = TreeSet(sections.keys)
133 for (sectionKey in sortedSections) {
134 val section = sections[sectionKey]
135 val settings = section!!.settings
136 val sortedKeySet: Set<String> = TreeSet(settings.keys)
137 for (settingKey in sortedKeySet) {
138 val setting = settings[settingKey]
139 NativeLibrary.setUserSetting(
140 gameId,
141 mapSectionNameFromIni(
142 section.name
143 ),
144 setting!!.key,
145 setting.valueAsString
146 )
147 }
148 }
149 }
150
151 private fun mapSectionNameFromIni(generalSectionName: String): String? {
152 return if (sectionsMap.getForward(generalSectionName) != null) {
153 sectionsMap.getForward(generalSectionName)
154 } else {
155 generalSectionName
156 }
157 }
158
159 private fun mapSectionNameToIni(generalSectionName: String): String {
160 return if (sectionsMap.getBackward(generalSectionName) != null) {
161 sectionsMap.getBackward(generalSectionName).toString()
162 } else {
163 generalSectionName
164 }
165 }
166
167 fun getSettingsFile(fileName: String): File {
168 return File(
169 DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini"
170 )
171 }
172
173 private fun getCustomGameSettingsFile(gameId: String): File {
174 return File(DirectoryInitialization.userDirectory + "/GameSettings/" + gameId + ".ini")
175 }
176
177 private fun sectionFromLine(line: String, isCustomGame: Boolean): SettingSection {
178 var sectionName: String = line.substring(1, line.length - 1)
179 if (isCustomGame) {
180 sectionName = mapSectionNameToIni(sectionName)
181 } 54 }
182 return SettingSection(sectionName)
183 } 55 }
184 56
185 /** 57 fun getSettingsFile(fileName: String): File =
186 * For a line of text, determines what type of data is being represented, and returns 58 File(DirectoryInitialization.userDirectory + "/config/" + fileName + ".ini")
187 * a Setting object containing this data.
188 *
189 * @param line The line of text being parsed.
190 * @return A typed Setting containing the key/value contained in the line.
191 */
192 private fun settingFromLine(line: String): AbstractSetting? {
193 val splitLine = line.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
194 if (splitLine.size != 2) {
195 return null
196 }
197 val key = splitLine[0].trim { it <= ' ' }
198 val value = splitLine[1].trim { it <= ' ' }
199 if (value.isEmpty()) {
200 return null
201 }
202
203 val booleanSetting = BooleanSetting.from(key)
204 if (booleanSetting != null) {
205 booleanSetting.boolean = value.toBoolean()
206 return booleanSetting
207 }
208
209 val intSetting = IntSetting.from(key)
210 if (intSetting != null) {
211 intSetting.int = value.toInt()
212 return intSetting
213 }
214
215 val floatSetting = FloatSetting.from(key)
216 if (floatSetting != null) {
217 floatSetting.float = value.toFloat()
218 return floatSetting
219 }
220
221 val stringSetting = StringSetting.from(key)
222 if (stringSetting != null) {
223 stringSetting.string = value
224 return stringSetting
225 }
226
227 return null
228 }
229
230 /**
231 * Writes the contents of a Section HashMap to disk.
232 *
233 * @param parser A Wini pointed at a file on disk.
234 * @param section A section containing settings to be written to the file.
235 */
236 private fun writeSection(parser: Wini, section: SettingSection) {
237 // Write the section header.
238 val header = section.name
239
240 // Write this section's values.
241 val settings = section.settings
242 val keySet: Set<String> = settings.keys
243 for (key in keySet) {
244 val setting = settings[key]
245 parser.put(header, setting!!.key, setting.valueAsString)
246 }
247
248 BooleanSetting.values().forEach {
249 if (!keySet.contains(it.key)) {
250 parser.put(header, it.key, it.valueAsString)
251 }
252 }
253 IntSetting.values().forEach {
254 if (!keySet.contains(it.key)) {
255 parser.put(header, it.key, it.valueAsString)
256 }
257 }
258 StringSetting.values().forEach {
259 if (!keySet.contains(it.key)) {
260 parser.put(header, it.key, it.valueAsString)
261 }
262 }
263 }
264} 59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 25b9d4018..944ae652e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -7,11 +7,11 @@ import android.annotation.SuppressLint
7import android.app.AlertDialog 7import android.app.AlertDialog
8import android.content.Context 8import android.content.Context
9import android.content.DialogInterface 9import android.content.DialogInterface
10import android.content.Intent
11import android.content.SharedPreferences 10import android.content.SharedPreferences
12import android.content.pm.ActivityInfo 11import android.content.pm.ActivityInfo
13import android.content.res.Configuration 12import android.content.res.Configuration
14import android.graphics.Color 13import android.graphics.Color
14import android.net.Uri
15import android.os.Bundle 15import android.os.Bundle
16import android.os.Handler 16import android.os.Handler
17import android.os.Looper 17import android.os.Looper
@@ -19,18 +19,18 @@ import android.util.Rational
19import android.view.* 19import android.view.*
20import android.widget.TextView 20import android.widget.TextView
21import androidx.activity.OnBackPressedCallback 21import androidx.activity.OnBackPressedCallback
22import androidx.activity.result.ActivityResultLauncher
23import androidx.activity.result.contract.ActivityResultContracts
24import androidx.appcompat.widget.PopupMenu 22import androidx.appcompat.widget.PopupMenu
25import androidx.core.content.res.ResourcesCompat 23import androidx.core.content.res.ResourcesCompat
26import androidx.core.graphics.Insets 24import androidx.core.graphics.Insets
27import androidx.core.view.ViewCompat 25import androidx.core.view.ViewCompat
28import androidx.core.view.WindowInsetsCompat 26import androidx.core.view.WindowInsetsCompat
29import androidx.core.view.isVisible 27import androidx.drawerlayout.widget.DrawerLayout
30import androidx.fragment.app.Fragment 28import androidx.fragment.app.Fragment
29import androidx.fragment.app.activityViewModels
31import androidx.lifecycle.Lifecycle 30import androidx.lifecycle.Lifecycle
32import androidx.lifecycle.lifecycleScope 31import androidx.lifecycle.lifecycleScope
33import androidx.lifecycle.repeatOnLifecycle 32import androidx.lifecycle.repeatOnLifecycle
33import androidx.navigation.findNavController
34import androidx.navigation.fragment.navArgs 34import androidx.navigation.fragment.navArgs
35import androidx.preference.PreferenceManager 35import androidx.preference.PreferenceManager
36import androidx.window.layout.FoldingFeature 36import androidx.window.layout.FoldingFeature
@@ -40,6 +40,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
40import com.google.android.material.slider.Slider 40import com.google.android.material.slider.Slider
41import kotlinx.coroutines.Dispatchers 41import kotlinx.coroutines.Dispatchers
42import kotlinx.coroutines.launch 42import kotlinx.coroutines.launch
43import org.yuzu.yuzu_emu.HomeNavigationDirections
43import org.yuzu.yuzu_emu.NativeLibrary 44import org.yuzu.yuzu_emu.NativeLibrary
44import org.yuzu.yuzu_emu.R 45import org.yuzu.yuzu_emu.R
45import org.yuzu.yuzu_emu.YuzuApplication 46import org.yuzu.yuzu_emu.YuzuApplication
@@ -48,8 +49,9 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
48import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding 49import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
49import org.yuzu.yuzu_emu.features.settings.model.IntSetting 50import org.yuzu.yuzu_emu.features.settings.model.IntSetting
50import org.yuzu.yuzu_emu.features.settings.model.Settings 51import org.yuzu.yuzu_emu.features.settings.model.Settings
51import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
52import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 52import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
53import org.yuzu.yuzu_emu.model.Game
54import org.yuzu.yuzu_emu.model.EmulationViewModel
53import org.yuzu.yuzu_emu.overlay.InputOverlay 55import org.yuzu.yuzu_emu.overlay.InputOverlay
54import org.yuzu.yuzu_emu.utils.* 56import org.yuzu.yuzu_emu.utils.*
55 57
@@ -62,11 +64,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
62 private var _binding: FragmentEmulationBinding? = null 64 private var _binding: FragmentEmulationBinding? = null
63 private val binding get() = _binding!! 65 private val binding get() = _binding!!
64 66
65 val args by navArgs<EmulationFragmentArgs>() 67 private val args by navArgs<EmulationFragmentArgs>()
66 68
67 private var isInFoldableLayout = false 69 private lateinit var game: Game
70
71 private val emulationViewModel: EmulationViewModel by activityViewModels()
68 72
69 private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent> 73 private var isInFoldableLayout = false
70 74
71 override fun onAttach(context: Context) { 75 override fun onAttach(context: Context) {
72 super.onAttach(context) 76 super.onAttach(context)
@@ -81,11 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
81 .collect { updateFoldableLayout(context, it) } 85 .collect { updateFoldableLayout(context, it) }
82 } 86 }
83 } 87 }
84
85 onReturnFromSettings = context.activityResultRegistry.register(
86 "SettingsResult",
87 ActivityResultContracts.StartActivityForResult()
88 ) { updateScreenLayout() }
89 } else { 88 } else {
90 throw IllegalStateException("EmulationFragment must have EmulationActivity parent") 89 throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
91 } 90 }
@@ -97,10 +96,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
97 override fun onCreate(savedInstanceState: Bundle?) { 96 override fun onCreate(savedInstanceState: Bundle?) {
98 super.onCreate(savedInstanceState) 97 super.onCreate(savedInstanceState)
99 98
99 val intentUri: Uri? = requireActivity().intent.data
100 var intentGame: Game? = null
101 if (intentUri != null) {
102 intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) {
103 GameHelper.getGame(requireActivity().intent.data!!, false)
104 } else {
105 null
106 }
107 }
108 game = if (args.game != null) {
109 args.game!!
110 } else {
111 intentGame ?: error("[EmulationFragment] No bootable game present!")
112 }
113
100 // So this fragment doesn't restart on configuration changes; i.e. rotation. 114 // So this fragment doesn't restart on configuration changes; i.e. rotation.
101 retainInstance = true 115 retainInstance = true
102 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 116 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
103 emulationState = EmulationState(args.game.path) 117 emulationState = EmulationState(game.path)
104 } 118 }
105 119
106 /** 120 /**
@@ -120,11 +134,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
120 binding.showFpsText.setTextColor(Color.YELLOW) 134 binding.showFpsText.setTextColor(Color.YELLOW)
121 binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } 135 binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
122 136
123 // Setup overlay. 137 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
124 updateShowFpsOverlay()
125
126 binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = 138 binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
127 args.game.title 139 game.title
128 binding.inGameMenu.setNavigationItemSelectedListener { 140 binding.inGameMenu.setNavigationItemSelectedListener {
129 when (it.itemId) { 141 when (it.itemId) {
130 R.id.menu_pause_emulation -> { 142 R.id.menu_pause_emulation -> {
@@ -149,12 +161,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
149 } 161 }
150 162
151 R.id.menu_settings -> { 163 R.id.menu_settings -> {
152 SettingsActivity.launch( 164 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
153 requireContext(), 165 null,
154 onReturnFromSettings, 166 SettingsFile.FILE_NAME_CONFIG
155 SettingsFile.FILE_NAME_CONFIG,
156 ""
157 ) 167 )
168 binding.root.findNavController().navigate(action)
158 true 169 true
159 } 170 }
160 171
@@ -165,7 +176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
165 176
166 R.id.menu_exit -> { 177 R.id.menu_exit -> {
167 emulationState.stop() 178 emulationState.stop()
168 requireActivity().finish() 179 emulationViewModel.setIsEmulationStopping(true)
180 binding.drawerLayout.close()
181 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
169 true 182 true
170 } 183 }
171 184
@@ -179,6 +192,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
179 requireActivity(), 192 requireActivity(),
180 object : OnBackPressedCallback(true) { 193 object : OnBackPressedCallback(true) {
181 override fun handleOnBackPressed() { 194 override fun handleOnBackPressed() {
195 if (!NativeLibrary.isRunning()) {
196 return
197 }
198
182 if (binding.drawerLayout.isOpen) { 199 if (binding.drawerLayout.isOpen) {
183 binding.drawerLayout.close() 200 binding.drawerLayout.close()
184 } else { 201 } else {
@@ -195,6 +212,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
195 .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) } 212 .collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
196 } 213 }
197 } 214 }
215
216 GameIconUtils.loadGameIcon(game, binding.loadingImage)
217 binding.loadingTitle.text = game.title
218 binding.loadingTitle.isSelected = true
219 binding.loadingText.isSelected = true
220
221 emulationViewModel.shaderProgress.observe(viewLifecycleOwner) {
222 if (it > 0 && it != emulationViewModel.totalShaders.value!!) {
223 binding.loadingProgressIndicator.isIndeterminate = false
224
225 if (it < binding.loadingProgressIndicator.max) {
226 binding.loadingProgressIndicator.progress = it
227 }
228 }
229
230 if (it == emulationViewModel.totalShaders.value!!) {
231 binding.loadingText.setText(R.string.loading)
232 binding.loadingProgressIndicator.isIndeterminate = true
233 }
234 }
235 emulationViewModel.totalShaders.observe(viewLifecycleOwner) {
236 binding.loadingProgressIndicator.max = it
237 }
238 emulationViewModel.shaderMessage.observe(viewLifecycleOwner) {
239 if (it.isNotEmpty()) {
240 binding.loadingText.text = it
241 }
242 }
243
244 emulationViewModel.emulationStarted.observe(viewLifecycleOwner) { started ->
245 if (started) {
246 binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
247 ViewUtils.showView(binding.surfaceInputOverlay)
248 ViewUtils.hideView(binding.loadingIndicator)
249
250 // Setup overlay
251 updateShowFpsOverlay()
252 }
253 }
254
255 emulationViewModel.isEmulationStopping.observe(viewLifecycleOwner) {
256 if (it) {
257 binding.loadingText.setText(R.string.shutting_down)
258 ViewUtils.showView(binding.loadingIndicator)
259 ViewUtils.hideView(binding.inputContainer)
260 ViewUtils.hideView(binding.showFpsText)
261 }
262 }
198 } 263 }
199 264
200 override fun onConfigurationChanged(newConfig: Configuration) { 265 override fun onConfigurationChanged(newConfig: Configuration) {
@@ -204,11 +269,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
204 binding.drawerLayout.close() 269 binding.drawerLayout.close()
205 } 270 }
206 if (EmulationMenuSettings.showOverlay) { 271 if (EmulationMenuSettings.showOverlay) {
207 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false } 272 binding.surfaceInputOverlay.post {
273 binding.surfaceInputOverlay.visibility = View.VISIBLE
274 }
208 } 275 }
209 } else { 276 } else {
210 if (EmulationMenuSettings.showOverlay) { 277 if (EmulationMenuSettings.showOverlay &&
211 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true } 278 emulationViewModel.emulationStarted.value == true
279 ) {
280 binding.surfaceInputOverlay.post {
281 binding.surfaceInputOverlay.visibility = View.VISIBLE
282 }
283 } else {
284 binding.surfaceInputOverlay.post {
285 binding.surfaceInputOverlay.visibility = View.INVISIBLE
286 }
212 } 287 }
213 if (!isInFoldableLayout) { 288 if (!isInFoldableLayout) {
214 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 289 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -217,16 +292,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
217 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE 292 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
218 } 293 }
219 } 294 }
220 if (!binding.surfaceInputOverlay.isInEditMode) {
221 refreshInputOverlay()
222 }
223 } 295 }
224 } 296 }
225 297
226 override fun onResume() { 298 override fun onResume() {
227 super.onResume() 299 super.onResume()
228 if (!DirectoryInitialization.areDirectoriesReady) { 300 if (!DirectoryInitialization.areDirectoriesReady) {
229 DirectoryInitialization.start(requireContext()) 301 DirectoryInitialization.start()
230 } 302 }
231 303
232 updateScreenLayout() 304 updateScreenLayout()
@@ -251,10 +323,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
251 super.onDetach() 323 super.onDetach()
252 } 324 }
253 325
254 private fun refreshInputOverlay() {
255 binding.surfaceInputOverlay.refreshControls()
256 }
257
258 private fun resetInputOverlay() { 326 private fun resetInputOverlay() {
259 preferences.edit() 327 preferences.edit()
260 .remove(Settings.PREF_CONTROL_SCALE) 328 .remove(Settings.PREF_CONTROL_SCALE)
@@ -272,17 +340,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
272 val FRAMETIME = 2 340 val FRAMETIME = 2
273 val SPEED = 3 341 val SPEED = 3
274 perfStatsUpdater = { 342 perfStatsUpdater = {
275 val perfStats = NativeLibrary.getPerfStats() 343 if (emulationViewModel.emulationStarted.value == true) {
276 if (perfStats[FPS] > 0 && _binding != null) { 344 val perfStats = NativeLibrary.getPerfStats()
277 binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS]) 345 if (perfStats[FPS] > 0 && _binding != null) {
278 } 346 binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
279 347 }
280 if (!emulationState.isStopped) {
281 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100) 348 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
282 } 349 }
283 } 350 }
284 perfStatsUpdateHandler.post(perfStatsUpdater!!) 351 perfStatsUpdateHandler.post(perfStatsUpdater!!)
285 binding.showFpsText.text = resources.getString(R.string.emulation_game_loading)
286 binding.showFpsText.visibility = View.VISIBLE 352 binding.showFpsText.visibility = View.VISIBLE
287 } else { 353 } else {
288 if (perfStatsUpdater != null) { 354 if (perfStatsUpdater != null) {
@@ -340,7 +406,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
340 406
341 isInFoldableLayout = true 407 isInFoldableLayout = true
342 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE 408 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
343 refreshInputOverlay()
344 } 409 }
345 } 410 }
346 it.isSeparating 411 it.isSeparating
@@ -428,7 +493,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
428 .apply() 493 .apply()
429 } 494 }
430 .setPositiveButton(android.R.string.ok) { _, _ -> 495 .setPositiveButton(android.R.string.ok) { _, _ ->
431 refreshInputOverlay() 496 binding.surfaceInputOverlay.refreshControls()
432 } 497 }
433 .setNegativeButton(android.R.string.cancel, null) 498 .setNegativeButton(android.R.string.cancel, null)
434 .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> } 499 .setNeutralButton(R.string.emulation_toggle_all) { _, _ -> }
@@ -452,7 +517,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
452 R.id.menu_show_overlay -> { 517 R.id.menu_show_overlay -> {
453 it.isChecked = !it.isChecked 518 it.isChecked = !it.isChecked
454 EmulationMenuSettings.showOverlay = it.isChecked 519 EmulationMenuSettings.showOverlay = it.isChecked
455 refreshInputOverlay() 520 binding.surfaceInputOverlay.refreshControls()
456 true 521 true
457 } 522 }
458 523
@@ -558,14 +623,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
558 preferences.edit() 623 preferences.edit()
559 .putInt(Settings.PREF_CONTROL_SCALE, scale) 624 .putInt(Settings.PREF_CONTROL_SCALE, scale)
560 .apply() 625 .apply()
561 refreshInputOverlay() 626 binding.surfaceInputOverlay.refreshControls()
562 } 627 }
563 628
564 private fun setControlOpacity(opacity: Int) { 629 private fun setControlOpacity(opacity: Int) {
565 preferences.edit() 630 preferences.edit()
566 .putInt(Settings.PREF_CONTROL_OPACITY, opacity) 631 .putInt(Settings.PREF_CONTROL_OPACITY, opacity)
567 .apply() 632 .apply()
568 refreshInputOverlay() 633 binding.surfaceInputOverlay.refreshControls()
569 } 634 }
570 635
571 private fun setInsets() { 636 private fun setInsets() {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index d5e793491..cbbe14d22 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -25,17 +25,18 @@ import androidx.core.view.updatePadding
25import androidx.documentfile.provider.DocumentFile 25import androidx.documentfile.provider.DocumentFile
26import androidx.fragment.app.Fragment 26import androidx.fragment.app.Fragment
27import androidx.fragment.app.activityViewModels 27import androidx.fragment.app.activityViewModels
28import androidx.navigation.findNavController
28import androidx.navigation.fragment.findNavController 29import androidx.navigation.fragment.findNavController
29import androidx.recyclerview.widget.LinearLayoutManager 30import androidx.recyclerview.widget.LinearLayoutManager
30import com.google.android.material.dialog.MaterialAlertDialogBuilder 31import com.google.android.material.dialog.MaterialAlertDialogBuilder
31import com.google.android.material.transition.MaterialSharedAxis 32import com.google.android.material.transition.MaterialSharedAxis
32import org.yuzu.yuzu_emu.BuildConfig 33import org.yuzu.yuzu_emu.BuildConfig
34import org.yuzu.yuzu_emu.HomeNavigationDirections
33import org.yuzu.yuzu_emu.R 35import org.yuzu.yuzu_emu.R
34import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter 36import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
35import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding 37import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
36import org.yuzu.yuzu_emu.features.DocumentProvider 38import org.yuzu.yuzu_emu.features.DocumentProvider
37import org.yuzu.yuzu_emu.features.settings.model.Settings 39import org.yuzu.yuzu_emu.features.settings.model.Settings
38import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
39import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 40import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
40import org.yuzu.yuzu_emu.model.HomeSetting 41import org.yuzu.yuzu_emu.model.HomeSetting
41import org.yuzu.yuzu_emu.model.HomeViewModel 42import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -74,7 +75,13 @@ class HomeSettingsFragment : Fragment() {
74 R.string.advanced_settings, 75 R.string.advanced_settings,
75 R.string.settings_description, 76 R.string.settings_description,
76 R.drawable.ic_settings, 77 R.drawable.ic_settings,
77 { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") } 78 {
79 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
80 null,
81 SettingsFile.FILE_NAME_CONFIG
82 )
83 binding.root.findNavController().navigate(action)
84 }
78 ) 85 )
79 ) 86 )
80 add( 87 add(
@@ -90,7 +97,13 @@ class HomeSettingsFragment : Fragment() {
90 R.string.preferences_theme, 97 R.string.preferences_theme,
91 R.string.theme_and_color_description, 98 R.string.theme_and_color_description,
92 R.drawable.ic_palette, 99 R.drawable.ic_palette,
93 { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } 100 {
101 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
102 null,
103 Settings.SECTION_THEME
104 )
105 binding.root.findNavController().navigate(action)
106 }
94 ) 107 )
95 ) 108 )
96 add( 109 add(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
index e1495ee8c..f38aeea53 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt
@@ -187,8 +187,8 @@ class ImportExportSavesFragment : DialogFragment() {
187 withContext(Dispatchers.Main) { 187 withContext(Dispatchers.Main) {
188 if (!validZip) { 188 if (!validZip) {
189 MessageDialogFragment.newInstance( 189 MessageDialogFragment.newInstance(
190 R.string.save_file_invalid_zip_structure, 190 titleId = R.string.save_file_invalid_zip_structure,
191 R.string.save_file_invalid_zip_structure_description 191 descriptionId = R.string.save_file_invalid_zip_structure_description
192 ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) 192 ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
193 return@withContext 193 return@withContext
194 } 194 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
index 739b26f99..181bd983a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
@@ -34,7 +34,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
34 when (val result = taskViewModel.result.value) { 34 when (val result = taskViewModel.result.value) {
35 is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show() 35 is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show()
36 is MessageDialogFragment -> result.show( 36 is MessageDialogFragment -> result.show(
37 parentFragmentManager, 37 requireActivity().supportFragmentManager,
38 MessageDialogFragment.TAG 38 MessageDialogFragment.TAG
39 ) 39 )
40 } 40 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt
deleted file mode 100644
index b29b627e9..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LongMessageDialogFragment.kt
+++ /dev/null
@@ -1,62 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.Intent
8import android.net.Uri
9import android.os.Bundle
10import androidx.fragment.app.DialogFragment
11import com.google.android.material.dialog.MaterialAlertDialogBuilder
12import org.yuzu.yuzu_emu.R
13
14class LongMessageDialogFragment : DialogFragment() {
15 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
16 val titleId = requireArguments().getInt(TITLE)
17 val description = requireArguments().getString(DESCRIPTION)
18 val helpLinkId = requireArguments().getInt(HELP_LINK)
19
20 val dialog = MaterialAlertDialogBuilder(requireContext())
21 .setPositiveButton(R.string.close, null)
22 .setTitle(titleId)
23 .setMessage(description)
24
25 if (helpLinkId != 0) {
26 dialog.setNeutralButton(R.string.learn_more) { _, _ ->
27 openLink(getString(helpLinkId))
28 }
29 }
30
31 return dialog.show()
32 }
33
34 private fun openLink(link: String) {
35 val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
36 startActivity(intent)
37 }
38
39 companion object {
40 const val TAG = "LongMessageDialogFragment"
41
42 private const val TITLE = "Title"
43 private const val DESCRIPTION = "Description"
44 private const val HELP_LINK = "Link"
45
46 fun newInstance(
47 titleId: Int,
48 description: String,
49 helpLinkId: Int = 0
50 ): LongMessageDialogFragment {
51 val dialog = LongMessageDialogFragment()
52 val bundle = Bundle()
53 bundle.apply {
54 putInt(TITLE, titleId)
55 putString(DESCRIPTION, description)
56 putInt(HELP_LINK, helpLinkId)
57 }
58 dialog.arguments = bundle
59 return dialog
60 }
61 }
62}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
index 2db38fdc2..7d1c2c8dd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt
@@ -13,14 +13,20 @@ import org.yuzu.yuzu_emu.R
13 13
14class MessageDialogFragment : DialogFragment() { 14class MessageDialogFragment : DialogFragment() {
15 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { 15 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
16 val titleId = requireArguments().getInt(TITLE) 16 val titleId = requireArguments().getInt(TITLE_ID)
17 val descriptionId = requireArguments().getInt(DESCRIPTION) 17 val titleString = requireArguments().getString(TITLE_STRING)!!
18 val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
19 val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
18 val helpLinkId = requireArguments().getInt(HELP_LINK) 20 val helpLinkId = requireArguments().getInt(HELP_LINK)
19 21
20 val dialog = MaterialAlertDialogBuilder(requireContext()) 22 val dialog = MaterialAlertDialogBuilder(requireContext())
21 .setPositiveButton(R.string.close, null) 23 .setPositiveButton(R.string.close, null)
22 .setTitle(titleId) 24
23 .setMessage(descriptionId) 25 if (titleId != 0) dialog.setTitle(titleId)
26 if (titleString.isNotEmpty()) dialog.setTitle(titleString)
27
28 if (descriptionId != 0) dialog.setMessage(descriptionId)
29 if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString)
24 30
25 if (helpLinkId != 0) { 31 if (helpLinkId != 0) {
26 dialog.setNeutralButton(R.string.learn_more) { _, _ -> 32 dialog.setNeutralButton(R.string.learn_more) { _, _ ->
@@ -39,20 +45,26 @@ class MessageDialogFragment : DialogFragment() {
39 companion object { 45 companion object {
40 const val TAG = "MessageDialogFragment" 46 const val TAG = "MessageDialogFragment"
41 47
42 private const val TITLE = "Title" 48 private const val TITLE_ID = "Title"
43 private const val DESCRIPTION = "Description" 49 private const val TITLE_STRING = "TitleString"
50 private const val DESCRIPTION_ID = "DescriptionId"
51 private const val DESCRIPTION_STRING = "DescriptionString"
44 private const val HELP_LINK = "Link" 52 private const val HELP_LINK = "Link"
45 53
46 fun newInstance( 54 fun newInstance(
47 titleId: Int, 55 titleId: Int = 0,
48 descriptionId: Int, 56 titleString: String = "",
57 descriptionId: Int = 0,
58 descriptionString: String = "",
49 helpLinkId: Int = 0 59 helpLinkId: Int = 0
50 ): MessageDialogFragment { 60 ): MessageDialogFragment {
51 val dialog = MessageDialogFragment() 61 val dialog = MessageDialogFragment()
52 val bundle = Bundle() 62 val bundle = Bundle()
53 bundle.apply { 63 bundle.apply {
54 putInt(TITLE, titleId) 64 putInt(TITLE_ID, titleId)
55 putInt(DESCRIPTION, descriptionId) 65 putString(TITLE_STRING, titleString)
66 putInt(DESCRIPTION_ID, descriptionId)
67 putString(DESCRIPTION_STRING, descriptionString)
56 putInt(HELP_LINK, helpLinkId) 68 putInt(HELP_LINK, helpLinkId)
57 } 69 }
58 dialog.arguments = bundle 70 dialog.arguments = bundle
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
new file mode 100644
index 000000000..d18ec6974
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsDialogFragment.kt
@@ -0,0 +1,235 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.app.Dialog
7import android.content.DialogInterface
8import android.os.Bundle
9import android.view.LayoutInflater
10import android.view.View
11import android.view.ViewGroup
12import androidx.fragment.app.DialogFragment
13import androidx.fragment.app.activityViewModels
14import androidx.lifecycle.Lifecycle
15import androidx.lifecycle.lifecycleScope
16import androidx.lifecycle.repeatOnLifecycle
17import com.google.android.material.dialog.MaterialAlertDialogBuilder
18import com.google.android.material.slider.Slider
19import kotlinx.coroutines.launch
20import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
22import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
23import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
24import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
25import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
26import org.yuzu.yuzu_emu.model.SettingsViewModel
27
28class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
29 private var type = 0
30 private var position = 0
31
32 private var defaultCancelListener =
33 DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> closeDialog() }
34
35 private val settingsViewModel: SettingsViewModel by activityViewModels()
36
37 private lateinit var sliderBinding: DialogSliderBinding
38
39 override fun onCreate(savedInstanceState: Bundle?) {
40 super.onCreate(savedInstanceState)
41 type = requireArguments().getInt(TYPE)
42 position = requireArguments().getInt(POSITION)
43
44 if (settingsViewModel.clickedItem == null) dismiss()
45 }
46
47 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
48 return when (type) {
49 TYPE_RESET_SETTING -> {
50 MaterialAlertDialogBuilder(requireContext())
51 .setMessage(R.string.reset_setting_confirmation)
52 .setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
53 settingsViewModel.clickedItem!!.setting.reset()
54 settingsViewModel.setAdapterItemChanged(position)
55 settingsViewModel.shouldSave = true
56 }
57 .setNegativeButton(android.R.string.cancel, null)
58 .create()
59 }
60
61 SettingsItem.TYPE_SINGLE_CHOICE -> {
62 val item = settingsViewModel.clickedItem as SingleChoiceSetting
63 val value = getSelectionForSingleChoiceValue(item)
64 MaterialAlertDialogBuilder(requireContext())
65 .setTitle(item.nameId)
66 .setSingleChoiceItems(item.choicesId, value, this)
67 .create()
68 }
69
70 SettingsItem.TYPE_SLIDER -> {
71 sliderBinding = DialogSliderBinding.inflate(layoutInflater)
72 val item = settingsViewModel.clickedItem as SliderSetting
73
74 settingsViewModel.setSliderTextValue(item.selectedValue.toFloat(), item.units)
75 sliderBinding.slider.apply {
76 valueFrom = item.min.toFloat()
77 valueTo = item.max.toFloat()
78 value = settingsViewModel.sliderProgress.value.toFloat()
79 addOnChangeListener { _: Slider, value: Float, _: Boolean ->
80 settingsViewModel.setSliderTextValue(value, item.units)
81 }
82 }
83
84 MaterialAlertDialogBuilder(requireContext())
85 .setTitle(item.nameId)
86 .setView(sliderBinding.root)
87 .setPositiveButton(android.R.string.ok, this)
88 .setNegativeButton(android.R.string.cancel, defaultCancelListener)
89 .create()
90 }
91
92 SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
93 val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
94 MaterialAlertDialogBuilder(requireContext())
95 .setTitle(item.nameId)
96 .setSingleChoiceItems(item.choices, item.selectValueIndex, this)
97 .create()
98 }
99
100 else -> super.onCreateDialog(savedInstanceState)
101 }
102 }
103
104 override fun onCreateView(
105 inflater: LayoutInflater,
106 container: ViewGroup?,
107 savedInstanceState: Bundle?
108 ): View? {
109 return when (type) {
110 SettingsItem.TYPE_SLIDER -> sliderBinding.root
111 else -> super.onCreateView(inflater, container, savedInstanceState)
112 }
113 }
114
115 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
116 super.onViewCreated(view, savedInstanceState)
117 when (type) {
118 SettingsItem.TYPE_SLIDER -> {
119 viewLifecycleOwner.lifecycleScope.launch {
120 repeatOnLifecycle(Lifecycle.State.CREATED) {
121 settingsViewModel.sliderTextValue.collect {
122 sliderBinding.textValue.text = it
123 }
124 }
125 repeatOnLifecycle(Lifecycle.State.CREATED) {
126 settingsViewModel.sliderProgress.collect {
127 sliderBinding.slider.value = it.toFloat()
128 }
129 }
130 }
131 }
132 }
133 }
134
135 override fun onClick(dialog: DialogInterface, which: Int) {
136 when (settingsViewModel.clickedItem) {
137 is SingleChoiceSetting -> {
138 val scSetting = settingsViewModel.clickedItem as SingleChoiceSetting
139 val value = getValueForSingleChoiceSelection(scSetting, which)
140 if (scSetting.selectedValue != value) {
141 settingsViewModel.shouldSave = true
142 }
143 scSetting.selectedValue = value
144 }
145
146 is StringSingleChoiceSetting -> {
147 val scSetting = settingsViewModel.clickedItem as StringSingleChoiceSetting
148 val value = scSetting.getValueAt(which)
149 if (scSetting.selectedValue != value) settingsViewModel.shouldSave = true
150 scSetting.selectedValue = value
151 }
152
153 is SliderSetting -> {
154 val sliderSetting = settingsViewModel.clickedItem as SliderSetting
155 if (sliderSetting.selectedValue != settingsViewModel.sliderProgress.value) {
156 settingsViewModel.shouldSave = true
157 }
158 sliderSetting.selectedValue = settingsViewModel.sliderProgress.value
159 }
160 }
161 closeDialog()
162 }
163
164 private fun closeDialog() {
165 settingsViewModel.setAdapterItemChanged(position)
166 settingsViewModel.clickedItem = null
167 settingsViewModel.setSliderProgress(-1f)
168 dismiss()
169 }
170
171 private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int {
172 val valuesId = item.valuesId
173 return if (valuesId > 0) {
174 val valuesArray = requireContext().resources.getIntArray(valuesId)
175 valuesArray[which]
176 } else {
177 which
178 }
179 }
180
181 private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int {
182 val value = item.selectedValue
183 val valuesId = item.valuesId
184 if (valuesId > 0) {
185 val valuesArray = requireContext().resources.getIntArray(valuesId)
186 for (index in valuesArray.indices) {
187 val current = valuesArray[index]
188 if (current == value) {
189 return index
190 }
191 }
192 } else {
193 return value
194 }
195 return -1
196 }
197
198 companion object {
199 const val TAG = "SettingsDialogFragment"
200
201 const val TYPE_RESET_SETTING = -1
202
203 const val TITLE = "Title"
204 const val TYPE = "Type"
205 const val POSITION = "Position"
206
207 fun newInstance(
208 settingsViewModel: SettingsViewModel,
209 clickedItem: SettingsItem,
210 type: Int,
211 position: Int
212 ): SettingsDialogFragment {
213 when (type) {
214 SettingsItem.TYPE_HEADER,
215 SettingsItem.TYPE_SWITCH,
216 SettingsItem.TYPE_SUBMENU,
217 SettingsItem.TYPE_DATETIME_SETTING,
218 SettingsItem.TYPE_RUNNABLE ->
219 throw IllegalArgumentException("[SettingsDialogFragment] Incompatible type!")
220
221 SettingsItem.TYPE_SLIDER -> settingsViewModel.setSliderProgress(
222 (clickedItem as SliderSetting).selectedValue.toFloat()
223 )
224 }
225 settingsViewModel.clickedItem = clickedItem
226
227 val args = Bundle()
228 args.putInt(TYPE, type)
229 args.putInt(POSITION, position)
230 val fragment = SettingsDialogFragment()
231 fragment.arguments = args
232 return fragment
233 }
234 }
235}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
new file mode 100644
index 000000000..55b6a0367
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
@@ -0,0 +1,184 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.fragments
5
6import android.content.Context
7import android.os.Bundle
8import android.view.LayoutInflater
9import android.view.View
10import android.view.ViewGroup
11import android.view.inputmethod.InputMethodManager
12import androidx.core.view.ViewCompat
13import androidx.core.view.WindowInsetsCompat
14import androidx.core.view.updatePadding
15import androidx.core.widget.doOnTextChanged
16import androidx.fragment.app.Fragment
17import androidx.fragment.app.activityViewModels
18import androidx.recyclerview.widget.LinearLayoutManager
19import com.google.android.material.divider.MaterialDividerItemDecoration
20import com.google.android.material.transition.MaterialSharedAxis
21import info.debatty.java.stringsimilarity.Cosine
22import org.yuzu.yuzu_emu.R
23import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
24import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
25import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
26import org.yuzu.yuzu_emu.model.SettingsViewModel
27import org.yuzu.yuzu_emu.utils.NativeConfig
28
29class SettingsSearchFragment : Fragment() {
30 private var _binding: FragmentSettingsSearchBinding? = null
31 private val binding get() = _binding!!
32
33 private var settingsAdapter: SettingsAdapter? = null
34
35 private val settingsViewModel: SettingsViewModel by activityViewModels()
36
37 override fun onCreate(savedInstanceState: Bundle?) {
38 super.onCreate(savedInstanceState)
39 enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
40 returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
41 }
42
43 override fun onCreateView(
44 inflater: LayoutInflater,
45 container: ViewGroup?,
46 savedInstanceState: Bundle?
47 ): View {
48 _binding = FragmentSettingsSearchBinding.inflate(layoutInflater)
49 return binding.root
50 }
51
52 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
53 super.onViewCreated(view, savedInstanceState)
54 settingsViewModel.setIsUsingSearch(true)
55
56 if (savedInstanceState != null) {
57 binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
58 }
59
60 settingsAdapter = SettingsAdapter(this, requireContext())
61
62 val dividerDecoration = MaterialDividerItemDecoration(
63 requireContext(),
64 LinearLayoutManager.VERTICAL
65 )
66 dividerDecoration.isLastItemDecorated = false
67 binding.settingsList.apply {
68 adapter = settingsAdapter
69 layoutManager = LinearLayoutManager(requireContext())
70 addItemDecoration(dividerDecoration)
71 }
72
73 focusSearch()
74
75 binding.backButton.setOnClickListener { settingsViewModel.setShouldNavigateBack(true) }
76 binding.searchBackground.setOnClickListener { focusSearch() }
77 binding.clearButton.setOnClickListener { binding.searchText.setText("") }
78 binding.searchText.doOnTextChanged { _, _, _, _ ->
79 search()
80 binding.settingsList.smoothScrollToPosition(0)
81 }
82 settingsViewModel.shouldReloadSettingsList.observe(viewLifecycleOwner) {
83 if (it) {
84 settingsViewModel.setShouldReloadSettingsList(false)
85 search()
86 }
87 }
88
89 search()
90
91 setInsets()
92 }
93
94 override fun onSaveInstanceState(outState: Bundle) {
95 super.onSaveInstanceState(outState)
96 outState.putString(SEARCH_TEXT, binding.searchText.text.toString())
97 }
98
99 private fun search() {
100 val searchTerm = binding.searchText.text.toString().lowercase()
101 binding.clearButton.visibility =
102 if (searchTerm.isEmpty()) View.INVISIBLE else View.VISIBLE
103 if (searchTerm.isEmpty()) {
104 binding.noResultsView.visibility = View.VISIBLE
105 settingsAdapter?.submitList(emptyList())
106 return
107 }
108
109 val baseList = SettingsItem.settingsItems
110 val similarityAlgorithm = if (searchTerm.length > 2) Cosine() else Cosine(1)
111 val sortedList: List<SettingsItem> = baseList.mapNotNull { item ->
112 val title = getString(item.value.nameId).lowercase()
113 val similarity = similarityAlgorithm.similarity(searchTerm, title)
114 if (similarity > 0.08) {
115 Pair(similarity, item)
116 } else {
117 null
118 }
119 }.sortedByDescending { it.first }.mapNotNull {
120 val item = it.second.value
121 val pairedSettingKey = item.setting.pairedSettingKey
122 val optionalSetting: SettingsItem? = if (pairedSettingKey.isNotEmpty()) {
123 val pairedSettingValue = NativeConfig.getBoolean(pairedSettingKey, false)
124 if (pairedSettingValue) it.second.value else null
125 } else {
126 it.second.value
127 }
128 optionalSetting
129 }
130 settingsAdapter?.submitList(sortedList)
131 binding.noResultsView.visibility =
132 if (sortedList.isEmpty()) View.VISIBLE else View.INVISIBLE
133 }
134
135 private fun focusSearch() {
136 binding.searchText.requestFocus()
137 val imm = requireActivity()
138 .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
139 imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
140 }
141
142 private fun setInsets() =
143 ViewCompat.setOnApplyWindowInsetsListener(
144 binding.root
145 ) { _: View, windowInsets: WindowInsetsCompat ->
146 val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)
147 val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
148 val topMargin = resources.getDimensionPixelSize(R.dimen.spacing_chip)
149
150 val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
151 val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
152
153 val leftInsets = barInsets.left + cutoutInsets.left
154 val rightInsets = barInsets.right + cutoutInsets.right
155
156 binding.settingsList.updatePadding(bottom = barInsets.bottom + extraListSpacing)
157 binding.frameSearch.updatePadding(
158 left = leftInsets + sideMargin,
159 top = barInsets.top + topMargin,
160 right = rightInsets + sideMargin
161 )
162 binding.noResultsView.updatePadding(
163 left = leftInsets,
164 right = rightInsets,
165 bottom = barInsets.bottom
166 )
167
168 val mlpSettingsList = binding.settingsList.layoutParams as ViewGroup.MarginLayoutParams
169 mlpSettingsList.leftMargin = leftInsets + sideMargin
170 mlpSettingsList.rightMargin = rightInsets + sideMargin
171 binding.settingsList.layoutParams = mlpSettingsList
172
173 val mlpDivider = binding.divider.layoutParams as ViewGroup.MarginLayoutParams
174 mlpDivider.leftMargin = leftInsets + sideMargin
175 mlpDivider.rightMargin = rightInsets + sideMargin
176 binding.divider.layoutParams = mlpDivider
177
178 windowInsets
179 }
180
181 companion object {
182 const val SEARCH_TEXT = "SearchText"
183 }
184}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
new file mode 100644
index 000000000..e35f51bc3
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6import androidx.lifecycle.LiveData
7import androidx.lifecycle.MutableLiveData
8import androidx.lifecycle.ViewModel
9
10class EmulationViewModel : ViewModel() {
11 private val _emulationStarted = MutableLiveData(false)
12 val emulationStarted: LiveData<Boolean> get() = _emulationStarted
13
14 private val _isEmulationStopping = MutableLiveData(false)
15 val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping
16
17 private val _shaderProgress = MutableLiveData(0)
18 val shaderProgress: LiveData<Int> get() = _shaderProgress
19
20 private val _totalShaders = MutableLiveData(0)
21 val totalShaders: LiveData<Int> get() = _totalShaders
22
23 private val _shaderMessage = MutableLiveData("")
24 val shaderMessage: LiveData<String> get() = _shaderMessage
25
26 fun setEmulationStarted(started: Boolean) {
27 _emulationStarted.postValue(started)
28 }
29
30 fun setIsEmulationStopping(value: Boolean) {
31 _isEmulationStopping.value = value
32 }
33
34 fun setShaderProgress(progress: Int) {
35 _shaderProgress.value = progress
36 }
37
38 fun setTotalShaders(max: Int) {
39 _totalShaders.value = max
40 }
41
42 fun setShaderMessage(msg: String) {
43 _shaderMessage.value = msg
44 }
45
46 fun updateProgress(msg: String, progress: Int, max: Int) {
47 setShaderMessage(msg)
48 setShaderProgress(progress)
49 setTotalShaders(max)
50 }
51
52 fun clear() {
53 _emulationStarted.value = false
54 _isEmulationStopping.value = false
55 _shaderProgress.value = 0
56 _totalShaders.value = 0
57 _shaderMessage.value = ""
58 }
59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
new file mode 100644
index 000000000..d16d15fa6
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
@@ -0,0 +1,96 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.model
5
6import androidx.lifecycle.LiveData
7import androidx.lifecycle.MutableLiveData
8import androidx.lifecycle.SavedStateHandle
9import androidx.lifecycle.ViewModel
10import org.yuzu.yuzu_emu.R
11import org.yuzu.yuzu_emu.YuzuApplication
12import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
13
14class SettingsViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
15 var game: Game? = null
16
17 var shouldSave = false
18
19 var clickedItem: SettingsItem? = null
20
21 private val _toolbarTitle = MutableLiveData("")
22 val toolbarTitle: LiveData<String> get() = _toolbarTitle
23
24 private val _shouldRecreate = MutableLiveData(false)
25 val shouldRecreate: LiveData<Boolean> get() = _shouldRecreate
26
27 private val _shouldNavigateBack = MutableLiveData(false)
28 val shouldNavigateBack: LiveData<Boolean> get() = _shouldNavigateBack
29
30 private val _shouldShowResetSettingsDialog = MutableLiveData(false)
31 val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog
32
33 private val _shouldReloadSettingsList = MutableLiveData(false)
34 val shouldReloadSettingsList: LiveData<Boolean> get() = _shouldReloadSettingsList
35
36 private val _isUsingSearch = MutableLiveData(false)
37 val isUsingSearch: LiveData<Boolean> get() = _isUsingSearch
38
39 val sliderProgress = savedStateHandle.getStateFlow(KEY_SLIDER_PROGRESS, -1)
40
41 val sliderTextValue = savedStateHandle.getStateFlow(KEY_SLIDER_TEXT_VALUE, "")
42
43 val adapterItemChanged = savedStateHandle.getStateFlow(KEY_ADAPTER_ITEM_CHANGED, -1)
44
45 fun setToolbarTitle(value: String) {
46 _toolbarTitle.value = value
47 }
48
49 fun setShouldRecreate(value: Boolean) {
50 _shouldRecreate.value = value
51 }
52
53 fun setShouldNavigateBack(value: Boolean) {
54 _shouldNavigateBack.value = value
55 }
56
57 fun setShouldShowResetSettingsDialog(value: Boolean) {
58 _shouldShowResetSettingsDialog.value = value
59 }
60
61 fun setShouldReloadSettingsList(value: Boolean) {
62 _shouldReloadSettingsList.value = value
63 }
64
65 fun setIsUsingSearch(value: Boolean) {
66 _isUsingSearch.value = value
67 }
68
69 fun setSliderTextValue(value: Float, units: String) {
70 savedStateHandle[KEY_SLIDER_PROGRESS] = value
71 savedStateHandle[KEY_SLIDER_TEXT_VALUE] = String.format(
72 YuzuApplication.appContext.getString(R.string.value_with_units),
73 value.toInt().toString(),
74 units
75 )
76 }
77
78 fun setSliderProgress(value: Float) {
79 savedStateHandle[KEY_SLIDER_PROGRESS] = value
80 }
81
82 fun setAdapterItemChanged(value: Int) {
83 savedStateHandle[KEY_ADAPTER_ITEM_CHANGED] = value
84 }
85
86 fun clear() {
87 game = null
88 shouldSave = false
89 }
90
91 companion object {
92 const val KEY_SLIDER_TEXT_VALUE = "SliderTextValue"
93 const val KEY_SLIDER_PROGRESS = "SliderProgress"
94 const val KEY_ADAPTER_ITEM_CHANGED = "AdapterItemChanged"
95 }
96}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index aaf3a0ec1..7d8e06ad8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -33,17 +33,15 @@ import java.io.IOException
33import kotlinx.coroutines.Dispatchers 33import kotlinx.coroutines.Dispatchers
34import kotlinx.coroutines.launch 34import kotlinx.coroutines.launch
35import kotlinx.coroutines.withContext 35import kotlinx.coroutines.withContext
36import org.yuzu.yuzu_emu.HomeNavigationDirections
36import org.yuzu.yuzu_emu.NativeLibrary 37import org.yuzu.yuzu_emu.NativeLibrary
37import org.yuzu.yuzu_emu.R 38import org.yuzu.yuzu_emu.R
38import org.yuzu.yuzu_emu.activities.EmulationActivity 39import org.yuzu.yuzu_emu.activities.EmulationActivity
39import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 40import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
40import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding 41import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
41import org.yuzu.yuzu_emu.features.settings.model.Settings 42import org.yuzu.yuzu_emu.features.settings.model.Settings
42import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
43import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
44import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 43import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
45import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 44import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
46import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
47import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 45import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
48import org.yuzu.yuzu_emu.model.GamesViewModel 46import org.yuzu.yuzu_emu.model.GamesViewModel
49import org.yuzu.yuzu_emu.model.HomeViewModel 47import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -54,7 +52,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
54 52
55 private val homeViewModel: HomeViewModel by viewModels() 53 private val homeViewModel: HomeViewModel by viewModels()
56 private val gamesViewModel: GamesViewModel by viewModels() 54 private val gamesViewModel: GamesViewModel by viewModels()
57 private val settingsViewModel: SettingsViewModel by viewModels()
58 55
59 override var themeId: Int = 0 56 override var themeId: Int = 0
60 57
@@ -62,8 +59,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
62 val splashScreen = installSplashScreen() 59 val splashScreen = installSplashScreen()
63 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } 60 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
64 61
65 settingsViewModel.settings.loadSettings()
66
67 ThemeHelper.setTheme(this) 62 ThemeHelper.setTheme(this)
68 63
69 super.onCreate(savedInstanceState) 64 super.onCreate(savedInstanceState)
@@ -109,11 +104,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
109 when (it.itemId) { 104 when (it.itemId) {
110 R.id.gamesFragment -> gamesViewModel.setShouldScrollToTop(true) 105 R.id.gamesFragment -> gamesViewModel.setShouldScrollToTop(true)
111 R.id.searchFragment -> gamesViewModel.setSearchFocused(true) 106 R.id.searchFragment -> gamesViewModel.setSearchFocused(true)
112 R.id.homeSettingsFragment -> SettingsActivity.launch( 107 R.id.homeSettingsFragment -> {
113 this, 108 val action = HomeNavigationDirections.actionGlobalSettingsActivity(
114 SettingsFile.FILE_NAME_CONFIG, 109 null,
115 "" 110 SettingsFile.FILE_NAME_CONFIG
116 ) 111 )
112 navHostFragment.navController.navigate(action)
113 }
117 } 114 }
118 } 115 }
119 116
@@ -303,8 +300,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
303 fun processKey(result: Uri): Boolean { 300 fun processKey(result: Uri): Boolean {
304 if (FileUtil.getExtension(result) != "keys") { 301 if (FileUtil.getExtension(result) != "keys") {
305 MessageDialogFragment.newInstance( 302 MessageDialogFragment.newInstance(
306 R.string.reading_keys_failure, 303 titleId = R.string.reading_keys_failure,
307 R.string.install_prod_keys_failure_extension_description 304 descriptionId = R.string.install_prod_keys_failure_extension_description
308 ).show(supportFragmentManager, MessageDialogFragment.TAG) 305 ).show(supportFragmentManager, MessageDialogFragment.TAG)
309 return false 306 return false
310 } 307 }
@@ -332,9 +329,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
332 return true 329 return true
333 } else { 330 } else {
334 MessageDialogFragment.newInstance( 331 MessageDialogFragment.newInstance(
335 R.string.invalid_keys_error, 332 titleId = R.string.invalid_keys_error,
336 R.string.install_keys_failure_description, 333 descriptionId = R.string.install_keys_failure_description,
337 R.string.dumping_keys_quickstart_link 334 helpLinkId = R.string.dumping_keys_quickstart_link
338 ).show(supportFragmentManager, MessageDialogFragment.TAG) 335 ).show(supportFragmentManager, MessageDialogFragment.TAG)
339 return false 336 return false
340 } 337 }
@@ -372,8 +369,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
372 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 369 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
373 messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) { 370 messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
374 MessageDialogFragment.newInstance( 371 MessageDialogFragment.newInstance(
375 R.string.firmware_installed_failure, 372 titleId = R.string.firmware_installed_failure,
376 R.string.firmware_installed_failure_description 373 descriptionId = R.string.firmware_installed_failure_description
377 ) 374 )
378 } else { 375 } else {
379 firmwarePath.deleteRecursively() 376 firmwarePath.deleteRecursively()
@@ -403,8 +400,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
403 400
404 if (FileUtil.getExtension(result) != "bin") { 401 if (FileUtil.getExtension(result) != "bin") {
405 MessageDialogFragment.newInstance( 402 MessageDialogFragment.newInstance(
406 R.string.reading_keys_failure, 403 titleId = R.string.reading_keys_failure,
407 R.string.install_amiibo_keys_failure_extension_description 404 descriptionId = R.string.install_amiibo_keys_failure_extension_description
408 ).show(supportFragmentManager, MessageDialogFragment.TAG) 405 ).show(supportFragmentManager, MessageDialogFragment.TAG)
409 return@registerForActivityResult 406 return@registerForActivityResult
410 } 407 }
@@ -430,9 +427,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
430 ).show() 427 ).show()
431 } else { 428 } else {
432 MessageDialogFragment.newInstance( 429 MessageDialogFragment.newInstance(
433 R.string.invalid_keys_error, 430 titleId = R.string.invalid_keys_error,
434 R.string.install_keys_failure_description, 431 descriptionId = R.string.install_keys_failure_description,
435 R.string.dumping_keys_quickstart_link 432 helpLinkId = R.string.dumping_keys_quickstart_link
436 ).show(supportFragmentManager, MessageDialogFragment.TAG) 433 ).show(supportFragmentManager, MessageDialogFragment.TAG)
437 } 434 }
438 } 435 }
@@ -504,96 +501,91 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
504 var errorBaseGame = 0 501 var errorBaseGame = 0
505 var errorExtension = 0 502 var errorExtension = 0
506 var errorOther = 0 503 var errorOther = 0
507 var errorTotal = 0 504 documents.forEach {
508 lifecycleScope.launch { 505 when (NativeLibrary.installFileToNand(it.toString())) {
509 documents.forEach { 506 NativeLibrary.InstallFileToNandResult.Success -> {
510 when (NativeLibrary.installFileToNand(it.toString())) { 507 installSuccess += 1
511 NativeLibrary.InstallFileToNandResult.Success -> {
512 installSuccess += 1
513 }
514
515 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
516 installOverwrite += 1
517 }
518
519 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
520 errorBaseGame += 1
521 }
522
523 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
524 errorExtension += 1
525 }
526
527 else -> {
528 errorOther += 1
529 }
530 } 508 }
531 } 509
532 withContext(Dispatchers.Main) { 510 NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
533 val separator = System.getProperty("line.separator") ?: "\n" 511 installOverwrite += 1
534 val installResult = StringBuilder()
535 if (installSuccess > 0) {
536 installResult.append(
537 getString(
538 R.string.install_game_content_success_install,
539 installSuccess
540 )
541 )
542 installResult.append(separator)
543 } 512 }
544 if (installOverwrite > 0) { 513
545 installResult.append( 514 NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
546 getString( 515 errorBaseGame += 1
547 R.string.install_game_content_success_overwrite,
548 installOverwrite
549 )
550 )
551 installResult.append(separator)
552 } 516 }
553 errorTotal = errorBaseGame + errorExtension + errorOther 517
554 if (errorTotal > 0) { 518 NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
555 installResult.append(separator) 519 errorExtension += 1
556 installResult.append( 520 }
557 getString( 521
558 R.string.install_game_content_failed_count, 522 else -> {
559 errorTotal 523 errorOther += 1
560 )
561 )
562 installResult.append(separator)
563 if (errorBaseGame > 0) {
564 installResult.append(separator)
565 installResult.append(
566 getString(R.string.install_game_content_failure_base)
567 )
568 installResult.append(separator)
569 }
570 if (errorExtension > 0) {
571 installResult.append(separator)
572 installResult.append(
573 getString(R.string.install_game_content_failure_file_extension)
574 )
575 installResult.append(separator)
576 }
577 if (errorOther > 0) {
578 installResult.append(
579 getString(R.string.install_game_content_failure_description)
580 )
581 installResult.append(separator)
582 }
583 LongMessageDialogFragment.newInstance(
584 R.string.install_game_content_failure,
585 installResult.toString().trim(),
586 R.string.install_game_content_help_link
587 ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
588 } else {
589 LongMessageDialogFragment.newInstance(
590 R.string.install_game_content_success,
591 installResult.toString().trim()
592 ).show(supportFragmentManager, LongMessageDialogFragment.TAG)
593 } 524 }
594 } 525 }
595 } 526 }
596 return@newInstance installSuccess + installOverwrite + errorTotal 527
528 val separator = System.getProperty("line.separator") ?: "\n"
529 val installResult = StringBuilder()
530 if (installSuccess > 0) {
531 installResult.append(
532 getString(
533 R.string.install_game_content_success_install,
534 installSuccess
535 )
536 )
537 installResult.append(separator)
538 }
539 if (installOverwrite > 0) {
540 installResult.append(
541 getString(
542 R.string.install_game_content_success_overwrite,
543 installOverwrite
544 )
545 )
546 installResult.append(separator)
547 }
548 val errorTotal: Int = errorBaseGame + errorExtension + errorOther
549 if (errorTotal > 0) {
550 installResult.append(separator)
551 installResult.append(
552 getString(
553 R.string.install_game_content_failed_count,
554 errorTotal
555 )
556 )
557 installResult.append(separator)
558 if (errorBaseGame > 0) {
559 installResult.append(separator)
560 installResult.append(
561 getString(R.string.install_game_content_failure_base)
562 )
563 installResult.append(separator)
564 }
565 if (errorExtension > 0) {
566 installResult.append(separator)
567 installResult.append(
568 getString(R.string.install_game_content_failure_file_extension)
569 )
570 installResult.append(separator)
571 }
572 if (errorOther > 0) {
573 installResult.append(
574 getString(R.string.install_game_content_failure_description)
575 )
576 installResult.append(separator)
577 }
578 return@newInstance MessageDialogFragment.newInstance(
579 titleId = R.string.install_game_content_failure,
580 descriptionString = installResult.toString().trim(),
581 helpLinkId = R.string.install_game_content_help_link
582 )
583 } else {
584 return@newInstance MessageDialogFragment.newInstance(
585 titleId = R.string.install_game_content_success,
586 descriptionString = installResult.toString().trim()
587 )
588 }
597 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) 589 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
598 } 590 }
599 } 591 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt
deleted file mode 100644
index 9cfda74ee..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BiMap.kt
+++ /dev/null
@@ -1,25 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6class BiMap<K, V> {
7 private val forward: MutableMap<K, V> = HashMap()
8 private val backward: MutableMap<V, K> = HashMap()
9
10 @Synchronized
11 fun add(key: K, value: V) {
12 forward[key] = value
13 backward[value] = key
14 }
15
16 @Synchronized
17 fun getForward(key: K): V? {
18 return forward[key]
19 }
20
21 @Synchronized
22 fun getBackward(key: V): K? {
23 return backward[key]
24 }
25}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
index 2ee63697e..3c9f6bad0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt
@@ -3,18 +3,18 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import android.content.Context
7import java.io.IOException 6import java.io.IOException
8import org.yuzu.yuzu_emu.NativeLibrary 7import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.YuzuApplication
9 9
10object DirectoryInitialization { 10object DirectoryInitialization {
11 private var userPath: String? = null 11 private var userPath: String? = null
12 12
13 var areDirectoriesReady: Boolean = false 13 var areDirectoriesReady: Boolean = false
14 14
15 fun start(context: Context) { 15 fun start() {
16 if (!areDirectoriesReady) { 16 if (!areDirectoriesReady) {
17 initializeInternalStorage(context) 17 initializeInternalStorage()
18 NativeLibrary.initializeEmulation() 18 NativeLibrary.initializeEmulation()
19 areDirectoriesReady = true 19 areDirectoriesReady = true
20 } 20 }
@@ -26,9 +26,9 @@ object DirectoryInitialization {
26 return userPath 26 return userPath
27 } 27 }
28 28
29 private fun initializeInternalStorage(context: Context) { 29 private fun initializeInternalStorage() {
30 try { 30 try {
31 userPath = context.getExternalFilesDir(null)!!.canonicalPath 31 userPath = YuzuApplication.appContext.getExternalFilesDir(null)!!.canonicalPath
32 NativeLibrary.setAppDirectory(userPath!!) 32 NativeLibrary.setAppDirectory(userPath!!)
33 } catch (e: IOException) { 33 } catch (e: IOException) {
34 e.printStackTrace() 34 e.printStackTrace()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
index f71d0a098..e0ee29c9b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
@@ -63,13 +63,13 @@ object GameHelper {
63 ) 63 )
64 } else { 64 } else {
65 if (Game.extensions.contains(FileUtil.getExtension(it.uri))) { 65 if (Game.extensions.contains(FileUtil.getExtension(it.uri))) {
66 games.add(getGame(it.uri)) 66 games.add(getGame(it.uri, true))
67 } 67 }
68 } 68 }
69 } 69 }
70 } 70 }
71 71
72 private fun getGame(uri: Uri): Game { 72 fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
73 val filePath = uri.toString() 73 val filePath = uri.toString()
74 var name = NativeLibrary.getTitle(filePath) 74 var name = NativeLibrary.getTitle(filePath)
75 75
@@ -94,11 +94,13 @@ object GameHelper {
94 NativeLibrary.isHomebrew(filePath) 94 NativeLibrary.isHomebrew(filePath)
95 ) 95 )
96 96
97 val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L) 97 if (addedToLibrary) {
98 if (addedTime == 0L) { 98 val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)
99 preferences.edit() 99 if (addedTime == 0L) {
100 .putLong(newGame.keyAddedToLibraryTime, System.currentTimeMillis()) 100 preferences.edit()
101 .apply() 101 .putLong(newGame.keyAddedToLibraryTime, System.currentTimeMillis())
102 .apply()
103 }
102 } 104 }
103 105
104 return newGame 106 return newGame
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
new file mode 100644
index 000000000..c0fe596d7
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.graphics.Bitmap
7import android.graphics.BitmapFactory
8import android.widget.ImageView
9import androidx.core.graphics.drawable.toDrawable
10import coil.ImageLoader
11import coil.decode.DataSource
12import coil.fetch.DrawableResult
13import coil.fetch.FetchResult
14import coil.fetch.Fetcher
15import coil.key.Keyer
16import coil.memory.MemoryCache
17import coil.request.ImageRequest
18import coil.request.Options
19import org.yuzu.yuzu_emu.NativeLibrary
20import org.yuzu.yuzu_emu.R
21import org.yuzu.yuzu_emu.YuzuApplication
22import org.yuzu.yuzu_emu.model.Game
23
24class GameIconFetcher(
25 private val game: Game,
26 private val options: Options
27) : Fetcher {
28 override suspend fun fetch(): FetchResult {
29 return DrawableResult(
30 drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
31 isSampled = false,
32 dataSource = DataSource.DISK
33 )
34 }
35
36 private fun decodeGameIcon(uri: String): Bitmap? {
37 val data = NativeLibrary.getIcon(uri)
38 return BitmapFactory.decodeByteArray(
39 data,
40 0,
41 data.size,
42 BitmapFactory.Options()
43 )
44 }
45
46 class Factory : Fetcher.Factory<Game> {
47 override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher =
48 GameIconFetcher(data, options)
49 }
50}
51
52class GameIconKeyer : Keyer<Game> {
53 override fun key(data: Game, options: Options): String = data.path
54}
55
56object GameIconUtils {
57 private val imageLoader = ImageLoader.Builder(YuzuApplication.appContext)
58 .components {
59 add(GameIconKeyer())
60 add(GameIconFetcher.Factory())
61 }
62 .memoryCache {
63 MemoryCache.Builder(YuzuApplication.appContext)
64 .maxSizePercent(0.25)
65 .build()
66 }
67 .build()
68
69 fun loadGameIcon(game: Game, imageView: ImageView) {
70 val request = ImageRequest.Builder(YuzuApplication.appContext)
71 .data(game)
72 .target(imageView)
73 .error(R.drawable.default_icon)
74 .build()
75 imageLoader.enqueue(request)
76 }
77}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
new file mode 100644
index 000000000..9425f8b99
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6object NativeConfig {
7 external fun getBoolean(key: String, getDefault: Boolean): Boolean
8 external fun setBoolean(key: String, value: Boolean)
9
10 external fun getByte(key: String, getDefault: Boolean): Byte
11 external fun setByte(key: String, value: Byte)
12
13 external fun getShort(key: String, getDefault: Boolean): Short
14 external fun setShort(key: String, value: Short)
15
16 external fun getInt(key: String, getDefault: Boolean): Int
17 external fun setInt(key: String, value: Int)
18
19 external fun getFloat(key: String, getDefault: Boolean): Float
20 external fun setFloat(key: String, value: Float)
21
22 external fun getLong(key: String, getDefault: Boolean): Long
23 external fun setLong(key: String, value: Long)
24
25 external fun getString(key: String, getDefault: Boolean): String
26 external fun setString(key: String, value: String)
27
28 external fun getIsRuntimeModifiable(key: String): Boolean
29
30 external fun getConfigHeader(category: Int): String
31
32 external fun getPairedSettingKey(key: String): String
33}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
index 685ccaa76..2f0868c63 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt
@@ -7,7 +7,6 @@ import android.content.Context
7import android.util.AttributeSet 7import android.util.AttributeSet
8import android.util.Rational 8import android.util.Rational
9import android.view.SurfaceView 9import android.view.SurfaceView
10import kotlin.math.roundToInt
11 10
12class FixedRatioSurfaceView @JvmOverloads constructor( 11class FixedRatioSurfaceView @JvmOverloads constructor(
13 context: Context, 12 context: Context,
@@ -22,27 +21,44 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
22 */ 21 */
23 fun setAspectRatio(ratio: Rational?) { 22 fun setAspectRatio(ratio: Rational?) {
24 aspectRatio = ratio?.toFloat() ?: 0f 23 aspectRatio = ratio?.toFloat() ?: 0f
24 requestLayout()
25 } 25 }
26 26
27 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 27 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
28 super.onMeasure(widthMeasureSpec, heightMeasureSpec) 28 val displayWidth: Float = MeasureSpec.getSize(widthMeasureSpec).toFloat()
29 val width = MeasureSpec.getSize(widthMeasureSpec) 29 val displayHeight: Float = MeasureSpec.getSize(heightMeasureSpec).toFloat()
30 val height = MeasureSpec.getSize(heightMeasureSpec)
31 if (aspectRatio != 0f) { 30 if (aspectRatio != 0f) {
32 val newWidth: Int 31 val displayAspect = displayWidth / displayHeight
33 val newHeight: Int 32 if (displayAspect < aspectRatio) {
34 if (height * aspectRatio < width) { 33 // Max out width
35 newWidth = (height * aspectRatio).roundToInt() 34 val halfHeight = displayHeight / 2
36 newHeight = height 35 val surfaceHeight = displayWidth / aspectRatio
36 val newTop: Float = halfHeight - (surfaceHeight / 2)
37 val newBottom: Float = halfHeight + (surfaceHeight / 2)
38 super.onMeasure(
39 widthMeasureSpec,
40 MeasureSpec.makeMeasureSpec(
41 newBottom.toInt() - newTop.toInt(),
42 MeasureSpec.EXACTLY
43 )
44 )
45 return
37 } else { 46 } else {
38 newWidth = width 47 // Max out height
39 newHeight = (width / aspectRatio).roundToInt() 48 val halfWidth = displayWidth / 2
49 val surfaceWidth = displayHeight * aspectRatio
50 val newLeft: Float = halfWidth - (surfaceWidth / 2)
51 val newRight: Float = halfWidth + (surfaceWidth / 2)
52 super.onMeasure(
53 MeasureSpec.makeMeasureSpec(
54 newRight.toInt() - newLeft.toInt(),
55 MeasureSpec.EXACTLY
56 ),
57 heightMeasureSpec
58 )
59 return
40 } 60 }
41 val left = (width - newWidth) / 2
42 val top = (height - newHeight) / 2
43 setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
44 } else {
45 setLeftTopRightBottom(0, 0, width, height)
46 } 61 }
62 super.onMeasure(widthMeasureSpec, heightMeasureSpec)
47 } 63 }
48} 64}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index e2ed08e9f..e15d1480b 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -14,6 +14,8 @@ add_library(yuzu-android SHARED
14 id_cache.cpp 14 id_cache.cpp
15 id_cache.h 15 id_cache.h
16 native.cpp 16 native.cpp
17 native_config.cpp
18 uisettings.cpp
17) 19)
18 20
19set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) 21set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 5e1f10f99..34b425cb4 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -11,22 +11,25 @@
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/settings.h" 13#include "common/settings.h"
14#include "common/settings_enums.h"
14#include "core/hle/service/acc/profile_manager.h" 15#include "core/hle/service/acc/profile_manager.h"
15#include "input_common/main.h" 16#include "input_common/main.h"
16#include "jni/config.h" 17#include "jni/config.h"
17#include "jni/default_ini.h" 18#include "jni/default_ini.h"
19#include "uisettings.h"
18 20
19namespace FS = Common::FS; 21namespace FS = Common::FS;
20 22
21Config::Config(std::optional<std::filesystem::path> config_path) 23Config::Config(const std::string& config_name, ConfigType config_type)
22 : config_loc{config_path.value_or(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "config.ini")}, 24 : type(config_type), global{config_type == ConfigType::GlobalConfig} {
23 config{std::make_unique<INIReader>(FS::PathToUTF8String(config_loc))} { 25 Initialize(config_name);
24 Reload();
25} 26}
26 27
27Config::~Config() = default; 28Config::~Config() = default;
28 29
29bool Config::LoadINI(const std::string& default_contents, bool retry) { 30bool Config::LoadINI(const std::string& default_contents, bool retry) {
31 void(FS::CreateParentDir(config_loc));
32 config = std::make_unique<INIReader>(FS::PathToUTF8String(config_loc));
30 const auto config_loc_str = FS::PathToUTF8String(config_loc); 33 const auto config_loc_str = FS::PathToUTF8String(config_loc);
31 if (config->ParseError() < 0) { 34 if (config->ParseError() < 0) {
32 if (retry) { 35 if (retry) {
@@ -144,7 +147,9 @@ void Config::ReadValues() {
144 Service::Account::MAX_USERS - 1); 147 Service::Account::MAX_USERS - 1);
145 148
146 // Disable docked mode by default on Android 149 // Disable docked mode by default on Android
147 Settings::values.use_docked_mode = config->GetBoolean("System", "use_docked_mode", false); 150 Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false)
151 ? Settings::ConsoleMode::Docked
152 : Settings::ConsoleMode::Handheld);
148 153
149 const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); 154 const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
150 if (rng_seed_enabled) { 155 if (rng_seed_enabled) {
@@ -298,9 +303,28 @@ void Config::ReadValues() {
298 303
299 // Network 304 // Network
300 ReadSetting("Network", Settings::values.network_interface); 305 ReadSetting("Network", Settings::values.network_interface);
306
307 // Android
308 ReadSetting("Android", AndroidSettings::values.picture_in_picture);
309 ReadSetting("Android", AndroidSettings::values.screen_layout);
301} 310}
302 311
303void Config::Reload() { 312void Config::Initialize(const std::string& config_name) {
313 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
314 const auto config_file = fmt::format("{}.ini", config_name);
315
316 switch (type) {
317 case ConfigType::GlobalConfig:
318 config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
319 break;
320 case ConfigType::PerGameConfig:
321 config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
322 break;
323 case ConfigType::InputProfile:
324 config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
325 LoadINI(DefaultINI::android_config_file);
326 return;
327 }
304 LoadINI(DefaultINI::android_config_file); 328 LoadINI(DefaultINI::android_config_file);
305 ReadValues(); 329 ReadValues();
306} 330}
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h
index 0d7d6e94d..e1e8f47ed 100644
--- a/src/android/app/src/main/jni/config.h
+++ b/src/android/app/src/main/jni/config.h
@@ -13,25 +13,35 @@
13class INIReader; 13class INIReader;
14 14
15class Config { 15class Config {
16 std::filesystem::path config_loc;
17 std::unique_ptr<INIReader> config;
18
19 bool LoadINI(const std::string& default_contents = "", bool retry = true); 16 bool LoadINI(const std::string& default_contents = "", bool retry = true);
20 void ReadValues();
21 17
22public: 18public:
23 explicit Config(std::optional<std::filesystem::path> config_path = std::nullopt); 19 enum class ConfigType {
20 GlobalConfig,
21 PerGameConfig,
22 InputProfile,
23 };
24
25 explicit Config(const std::string& config_name = "config",
26 ConfigType config_type = ConfigType::GlobalConfig);
24 ~Config(); 27 ~Config();
25 28
26 void Reload(); 29 void Initialize(const std::string& config_name);
27 30
28private: 31private:
29 /** 32 /**
30 * Applies a value read from the sdl2_config to a Setting. 33 * Applies a value read from the config to a Setting.
31 * 34 *
32 * @param group The name of the INI group 35 * @param group The name of the INI group
33 * @param setting The yuzu setting to modify 36 * @param setting The yuzu setting to modify
34 */ 37 */
35 template <typename Type, bool ranged> 38 template <typename Type, bool ranged>
36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); 39 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
40
41 void ReadValues();
42
43 const ConfigType type;
44 std::unique_ptr<INIReader> config;
45 std::string config_loc;
46 const bool global;
37}; 47};
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 9cbbf23a3..960abf95a 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -15,6 +15,8 @@ static jclass s_disk_cache_progress_class;
15static jclass s_load_callback_stage_class; 15static jclass s_load_callback_stage_class;
16static jmethodID s_exit_emulation_activity; 16static jmethodID s_exit_emulation_activity;
17static jmethodID s_disk_cache_load_progress; 17static jmethodID s_disk_cache_load_progress;
18static jmethodID s_on_emulation_started;
19static jmethodID s_on_emulation_stopped;
18 20
19static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 21static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
20 22
@@ -59,6 +61,14 @@ jmethodID GetDiskCacheLoadProgress() {
59 return s_disk_cache_load_progress; 61 return s_disk_cache_load_progress;
60} 62}
61 63
64jmethodID GetOnEmulationStarted() {
65 return s_on_emulation_started;
66}
67
68jmethodID GetOnEmulationStopped() {
69 return s_on_emulation_stopped;
70}
71
62} // namespace IDCache 72} // namespace IDCache
63 73
64#ifdef __cplusplus 74#ifdef __cplusplus
@@ -85,6 +95,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
85 env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V"); 95 env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
86 s_disk_cache_load_progress = 96 s_disk_cache_load_progress =
87 env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V"); 97 env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
98 s_on_emulation_started =
99 env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
100 s_on_emulation_stopped =
101 env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
88 102
89 // Initialize Android Storage 103 // Initialize Android Storage
90 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 104 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index be535fe1e..b76158928 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -15,5 +15,7 @@ jclass GetDiskCacheProgressClass();
15jclass GetDiskCacheLoadCallbackStageClass(); 15jclass GetDiskCacheLoadCallbackStageClass();
16jmethodID GetExitEmulationActivity(); 16jmethodID GetExitEmulationActivity();
17jmethodID GetDiskCacheLoadProgress(); 17jmethodID GetDiskCacheLoadProgress();
18jmethodID GetOnEmulationStarted();
19jmethodID GetOnEmulationStopped();
18 20
19} // namespace IDCache 21} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index 8b99d1d6e..b9ecefa74 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -203,12 +203,10 @@ public:
203 } 203 }
204 204
205 bool IsRunning() const { 205 bool IsRunning() const {
206 std::scoped_lock lock(m_mutex);
207 return m_is_running; 206 return m_is_running;
208 } 207 }
209 208
210 bool IsPaused() const { 209 bool IsPaused() const {
211 std::scoped_lock lock(m_mutex);
212 return m_is_running && m_is_paused; 210 return m_is_running && m_is_paused;
213 } 211 }
214 212
@@ -272,6 +270,7 @@ public:
272 m_vulkan_library); 270 m_vulkan_library);
273 271
274 m_system.SetFilesystem(m_vfs); 272 m_system.SetFilesystem(m_vfs);
273 m_system.GetUserChannel().clear();
275 274
276 // Initialize system. 275 // Initialize system.
277 jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>(); 276 jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
@@ -335,6 +334,8 @@ public:
335 334
336 // Tear down the render window. 335 // Tear down the render window.
337 m_window.reset(); 336 m_window.reset();
337
338 OnEmulationStopped(m_load_result);
338 } 339 }
339 340
340 void PauseEmulation() { 341 void PauseEmulation() {
@@ -376,6 +377,8 @@ public:
376 m_system.InitializeDebugger(); 377 m_system.InitializeDebugger();
377 } 378 }
378 379
380 OnEmulationStarted();
381
379 while (true) { 382 while (true) {
380 { 383 {
381 [[maybe_unused]] std::unique_lock lock(m_mutex); 384 [[maybe_unused]] std::unique_lock lock(m_mutex);
@@ -420,7 +423,7 @@ public:
420 return false; 423 return false;
421 } 424 }
422 425
423 return !Settings::values.use_docked_mode.GetValue(); 426 return !Settings::IsDockedMode();
424 } 427 }
425 428
426 void SetDeviceType([[maybe_unused]] int index, int type) { 429 void SetDeviceType([[maybe_unused]] int index, int type) {
@@ -511,6 +514,18 @@ private:
511 static_cast<jint>(progress), static_cast<jint>(max)); 514 static_cast<jint>(progress), static_cast<jint>(max));
512 } 515 }
513 516
517 static void OnEmulationStarted() {
518 JNIEnv* env = IDCache::GetEnvForThread();
519 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
520 IDCache::GetOnEmulationStarted());
521 }
522
523 static void OnEmulationStopped(Core::SystemResultStatus result) {
524 JNIEnv* env = IDCache::GetEnvForThread();
525 env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
526 IDCache::GetOnEmulationStopped(), static_cast<jint>(result));
527 }
528
514private: 529private:
515 static EmulationSession s_instance; 530 static EmulationSession s_instance;
516 531
@@ -528,8 +543,8 @@ private:
528 Core::PerfStatsResults m_perf_stats{}; 543 Core::PerfStatsResults m_perf_stats{};
529 std::shared_ptr<FileSys::VfsFilesystem> m_vfs; 544 std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
530 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized}; 545 Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
531 bool m_is_running{}; 546 std::atomic<bool> m_is_running = false;
532 bool m_is_paused{}; 547 std::atomic<bool> m_is_paused = false;
533 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; 548 SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
534 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; 549 std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
535 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; 550 std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
@@ -824,34 +839,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass cl
824 Config{}; 839 Config{};
825} 840}
826 841
827jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
828 jstring j_game_id, jstring j_section,
829 jstring j_key) {
830 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
831 std::string_view section = env->GetStringUTFChars(j_section, 0);
832 std::string_view key = env->GetStringUTFChars(j_key, 0);
833
834 env->ReleaseStringUTFChars(j_game_id, game_id.data());
835 env->ReleaseStringUTFChars(j_section, section.data());
836 env->ReleaseStringUTFChars(j_key, key.data());
837
838 return env->NewStringUTF("");
839}
840
841void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
842 jstring j_game_id, jstring j_section,
843 jstring j_key, jstring j_value) {
844 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
845 std::string_view section = env->GetStringUTFChars(j_section, 0);
846 std::string_view key = env->GetStringUTFChars(j_key, 0);
847 std::string_view value = env->GetStringUTFChars(j_value, 0);
848
849 env->ReleaseStringUTFChars(j_game_id, game_id.data());
850 env->ReleaseStringUTFChars(j_section, section.data());
851 env->ReleaseStringUTFChars(j_key, key.data());
852 env->ReleaseStringUTFChars(j_value, value.data());
853}
854
855void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, 842void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
856 jstring j_game_id) { 843 jstring j_game_id) {
857 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); 844 std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
new file mode 100644
index 000000000..8a704960c
--- /dev/null
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -0,0 +1,237 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <string>
5
6#include <jni.h>
7
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "jni/android_common/android_common.h"
11#include "jni/config.h"
12#include "uisettings.h"
13
14template <typename T>
15Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
16 auto key = GetJString(env, jkey);
17 auto basicSetting = Settings::values.linkage.by_key[key];
18 auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key];
19 if (basicSetting != 0) {
20 return static_cast<Settings::Setting<T>*>(basicSetting);
21 }
22 if (basicAndroidSetting != 0) {
23 return static_cast<Settings::Setting<T>*>(basicAndroidSetting);
24 }
25 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
26 return nullptr;
27}
28
29extern "C" {
30
31jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
32 jstring jkey, jboolean getDefault) {
33 auto setting = getSetting<bool>(env, jkey);
34 if (setting == nullptr) {
35 return false;
36 }
37 setting->SetGlobal(true);
38
39 if (static_cast<bool>(getDefault)) {
40 return setting->GetDefault();
41 }
42
43 return setting->GetValue();
44}
45
46void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setBoolean(JNIEnv* env, jobject obj, jstring jkey,
47 jboolean value) {
48 auto setting = getSetting<bool>(env, jkey);
49 if (setting == nullptr) {
50 return;
51 }
52 setting->SetGlobal(true);
53 setting->SetValue(static_cast<bool>(value));
54}
55
56jbyte Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getByte(JNIEnv* env, jobject obj, jstring jkey,
57 jboolean getDefault) {
58 auto setting = getSetting<u8>(env, jkey);
59 if (setting == nullptr) {
60 return -1;
61 }
62 setting->SetGlobal(true);
63
64 if (static_cast<bool>(getDefault)) {
65 return setting->GetDefault();
66 }
67
68 return setting->GetValue();
69}
70
71void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setByte(JNIEnv* env, jobject obj, jstring jkey,
72 jbyte value) {
73 auto setting = getSetting<u8>(env, jkey);
74 if (setting == nullptr) {
75 return;
76 }
77 setting->SetGlobal(true);
78 setting->SetValue(value);
79}
80
81jshort Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getShort(JNIEnv* env, jobject obj, jstring jkey,
82 jboolean getDefault) {
83 auto setting = getSetting<u16>(env, jkey);
84 if (setting == nullptr) {
85 return -1;
86 }
87 setting->SetGlobal(true);
88
89 if (static_cast<bool>(getDefault)) {
90 return setting->GetDefault();
91 }
92
93 return setting->GetValue();
94}
95
96void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject obj, jstring jkey,
97 jshort value) {
98 auto setting = getSetting<u16>(env, jkey);
99 if (setting == nullptr) {
100 return;
101 }
102 setting->SetGlobal(true);
103 setting->SetValue(value);
104}
105
106jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
107 jboolean getDefault) {
108 auto setting = getSetting<int>(env, jkey);
109 if (setting == nullptr) {
110 return -1;
111 }
112 setting->SetGlobal(true);
113
114 if (static_cast<bool>(getDefault)) {
115 return setting->GetDefault();
116 }
117
118 return setting->GetValue();
119}
120
121void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setInt(JNIEnv* env, jobject obj, jstring jkey,
122 jint value) {
123 auto setting = getSetting<int>(env, jkey);
124 if (setting == nullptr) {
125 return;
126 }
127 setting->SetGlobal(true);
128 setting->SetValue(value);
129}
130
131jfloat Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getFloat(JNIEnv* env, jobject obj, jstring jkey,
132 jboolean getDefault) {
133 auto setting = getSetting<float>(env, jkey);
134 if (setting == nullptr) {
135 return -1;
136 }
137 setting->SetGlobal(true);
138
139 if (static_cast<bool>(getDefault)) {
140 return setting->GetDefault();
141 }
142
143 return setting->GetValue();
144}
145
146void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setFloat(JNIEnv* env, jobject obj, jstring jkey,
147 jfloat value) {
148 auto setting = getSetting<float>(env, jkey);
149 if (setting == nullptr) {
150 return;
151 }
152 setting->SetGlobal(true);
153 setting->SetValue(value);
154}
155
156jlong Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getLong(JNIEnv* env, jobject obj, jstring jkey,
157 jboolean getDefault) {
158 auto setting = getSetting<long>(env, jkey);
159 if (setting == nullptr) {
160 return -1;
161 }
162 setting->SetGlobal(true);
163
164 if (static_cast<bool>(getDefault)) {
165 return setting->GetDefault();
166 }
167
168 return setting->GetValue();
169}
170
171void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setLong(JNIEnv* env, jobject obj, jstring jkey,
172 jlong value) {
173 auto setting = getSetting<long>(env, jkey);
174 if (setting == nullptr) {
175 return;
176 }
177 setting->SetGlobal(true);
178 setting->SetValue(value);
179}
180
181jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getString(JNIEnv* env, jobject obj, jstring jkey,
182 jboolean getDefault) {
183 auto setting = getSetting<std::string>(env, jkey);
184 if (setting == nullptr) {
185 return ToJString(env, "");
186 }
187 setting->SetGlobal(true);
188
189 if (static_cast<bool>(getDefault)) {
190 return ToJString(env, setting->GetDefault());
191 }
192
193 return ToJString(env, setting->GetValue());
194}
195
196void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setString(JNIEnv* env, jobject obj, jstring jkey,
197 jstring value) {
198 auto setting = getSetting<std::string>(env, jkey);
199 if (setting == nullptr) {
200 return;
201 }
202
203 setting->SetGlobal(true);
204 setting->SetValue(GetJString(env, value));
205}
206
207jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsRuntimeModifiable(JNIEnv* env, jobject obj,
208 jstring jkey) {
209 auto key = GetJString(env, jkey);
210 auto setting = Settings::values.linkage.by_key[key];
211 if (setting != 0) {
212 return setting->RuntimeModfiable();
213 }
214 LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
215 return true;
216}
217
218jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getConfigHeader(JNIEnv* env, jobject obj,
219 jint jcategory) {
220 auto category = static_cast<Settings::Category>(jcategory);
221 return ToJString(env, Settings::TranslateCategory(category));
222}
223
224jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getPairedSettingKey(JNIEnv* env, jobject obj,
225 jstring jkey) {
226 auto setting = getSetting<std::string>(env, jkey);
227 if (setting == nullptr) {
228 return ToJString(env, "");
229 }
230 if (setting->PairedSetting() == nullptr) {
231 return ToJString(env, "");
232 }
233
234 return ToJString(env, setting->PairedSetting()->GetLabel());
235}
236
237} // extern "C"
diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/uisettings.cpp
new file mode 100644
index 000000000..f2f0bad50
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.cpp
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "uisettings.h"
5
6namespace AndroidSettings {
7
8Values values;
9
10} // namespace AndroidSettings
diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/uisettings.h
new file mode 100644
index 000000000..494654af7
--- /dev/null
+++ b/src/android/app/src/main/jni/uisettings.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <common/settings_common.h>
7#include "common/common_types.h"
8#include "common/settings_setting.h"
9
10namespace AndroidSettings {
11
12struct Values {
13 Settings::Linkage linkage;
14
15 // Android
16 Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture",
17 Settings::Category::Android};
18 Settings::Setting<s32> screen_layout{linkage,
19 5,
20 "screen_layout",
21 Settings::Category::Android,
22 Settings::Specialization::Default,
23 true,
24 true};
25};
26
27extern Values values;
28
29} // namespace AndroidSettings
diff --git a/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml b/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml
deleted file mode 100644
index 9f49c133a..000000000
--- a/src/android/app/src/main/res/anim-ldrtl/anim_pop_settings_fragment_out.xml
+++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<set xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <alpha
5 android:duration="125"
6 android:interpolator="@android:anim/decelerate_interpolator"
7 android:fromAlpha="1"
8 android:toAlpha="0" />
9
10 <translate
11 android:duration="125"
12 android:interpolator="@android:anim/decelerate_interpolator"
13 android:fromXDelta="0"
14 android:toXDelta="-75" />
15
16</set>
diff --git a/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml
deleted file mode 100644
index 82fd719db..000000000
--- a/src/android/app/src/main/res/anim-ldrtl/anim_settings_fragment_in.xml
+++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<set xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <alpha
5 android:duration="@android:integer/config_shortAnimTime"
6 android:interpolator="@android:anim/decelerate_interpolator"
7 android:fromAlpha="0"
8 android:toAlpha="1" />
9
10 <translate
11 android:duration="@android:integer/config_shortAnimTime"
12 android:interpolator="@android:anim/decelerate_interpolator"
13 android:fromXDelta="-200"
14 android:toXDelta="0" />
15
16</set>
diff --git a/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml
deleted file mode 100644
index 5892128f1..000000000
--- a/src/android/app/src/main/res/anim/anim_pop_settings_fragment_out.xml
+++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<set xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <alpha
5 android:duration="125"
6 android:interpolator="@android:anim/decelerate_interpolator"
7 android:fromAlpha="1"
8 android:toAlpha="0" />
9
10 <translate
11 android:duration="125"
12 android:interpolator="@android:anim/decelerate_interpolator"
13 android:fromXDelta="0"
14 android:toXDelta="75" />
15
16</set>
diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml
deleted file mode 100644
index 98e0cf8bd..000000000
--- a/src/android/app/src/main/res/anim/anim_settings_fragment_in.xml
+++ /dev/null
@@ -1,16 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<set xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <alpha
5 android:duration="@android:integer/config_shortAnimTime"
6 android:interpolator="@android:anim/decelerate_interpolator"
7 android:fromAlpha="0"
8 android:toAlpha="1" />
9
10 <translate
11 android:duration="@android:integer/config_shortAnimTime"
12 android:interpolator="@android:anim/decelerate_interpolator"
13 android:fromXDelta="200"
14 android:toXDelta="0" />
15
16</set>
diff --git a/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml b/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml
deleted file mode 100644
index 77a40a4d1..000000000
--- a/src/android/app/src/main/res/anim/anim_settings_fragment_out.xml
+++ /dev/null
@@ -1,10 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<set xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <alpha
5 android:duration="@android:integer/config_shortAnimTime"
6 android:interpolator="@android:anim/decelerate_interpolator"
7 android:fromAlpha="1"
8 android:toAlpha="0" />
9
10</set>
diff --git a/src/android/app/src/main/res/animator/menu_slide_in_from_start.xml b/src/android/app/src/main/res/animator/menu_slide_in_from_start.xml
deleted file mode 100644
index 4612aee13..000000000
--- a/src/android/app/src/main/res/animator/menu_slide_in_from_start.xml
+++ /dev/null
@@ -1,20 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<set xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <objectAnimator
5 android:propertyName="translationX"
6 android:valueType="floatType"
7 android:valueFrom="-1280dp"
8 android:valueTo="0"
9 android:interpolator="@android:interpolator/decelerate_quad"
10 android:duration="300"/>
11
12 <objectAnimator
13 android:propertyName="alpha"
14 android:valueType="floatType"
15 android:valueFrom="0"
16 android:valueTo="1"
17 android:interpolator="@android:interpolator/accelerate_quad"
18 android:duration="300"/>
19
20</set> \ No newline at end of file
diff --git a/src/android/app/src/main/res/animator/menu_slide_out_to_start.xml b/src/android/app/src/main/res/animator/menu_slide_out_to_start.xml
deleted file mode 100644
index c00478946..000000000
--- a/src/android/app/src/main/res/animator/menu_slide_out_to_start.xml
+++ /dev/null
@@ -1,21 +0,0 @@
1<?xml version="1.0" encoding="utf-8"?>
2<set xmlns:android="http://schemas.android.com/apk/res/android">
3
4 <!-- This animation is used ONLY when a submenu is replaced. -->
5 <objectAnimator
6 android:propertyName="translationX"
7 android:valueType="floatType"
8 android:valueFrom="0"
9 android:valueTo="-1280dp"
10 android:interpolator="@android:interpolator/decelerate_quad"
11 android:duration="200"/>
12
13 <objectAnimator
14 android:propertyName="alpha"
15 android:valueType="floatType"
16 android:valueFrom="1"
17 android:valueTo="0"
18 android:interpolator="@android:interpolator/decelerate_quad"
19 android:duration="200"/>
20
21</set> \ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml
index 14ae83b04..8a026a30a 100644
--- a/src/android/app/src/main/res/layout/activity_settings.xml
+++ b/src/android/app/src/main/res/layout/activity_settings.xml
@@ -1,42 +1,24 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<androidx.coordinatorlayout.widget.CoordinatorLayout 2<androidx.constraintlayout.widget.ConstraintLayout
3 android:id="@+id/coordinator_main"
4 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:android="http://schemas.android.com/apk/res/android"
5 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:id="@+id/constraint_settings"
6 android:layout_width="match_parent" 7 android:layout_width="match_parent"
7 android:layout_height="match_parent" 8 android:layout_height="match_parent"
8 android:background="?attr/colorSurface"> 9 android:background="?attr/colorSurface">
9 10
10 <com.google.android.material.appbar.AppBarLayout 11 <androidx.fragment.app.FragmentContainerView
11 android:id="@+id/appbar_settings" 12 android:id="@+id/fragment_container"
12 android:layout_width="match_parent" 13 android:name="androidx.navigation.fragment.NavHostFragment"
13 android:layout_height="wrap_content" 14 android:layout_width="0dp"
14 android:fitsSystemWindows="true" 15 android:layout_height="0dp"
15 app:elevation="0dp"> 16 app:defaultNavHost="true"
16 17 app:layout_constraintBottom_toBottomOf="parent"
17 <com.google.android.material.appbar.CollapsingToolbarLayout 18 app:layout_constraintLeft_toLeftOf="parent"
18 style="?attr/collapsingToolbarLayoutMediumStyle" 19 app:layout_constraintRight_toRightOf="parent"
19 android:id="@+id/toolbar_settings_layout" 20 app:layout_constraintTop_toTopOf="parent"
20 android:layout_width="match_parent" 21 tools:layout="@layout/fragment_settings" />
21 android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
22 app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
23
24 <com.google.android.material.appbar.MaterialToolbar
25 android:id="@+id/toolbar_settings"
26 android:layout_width="match_parent"
27 android:layout_height="?attr/actionBarSize"
28 app:layout_collapseMode="pin" />
29
30 </com.google.android.material.appbar.CollapsingToolbarLayout>
31
32 </com.google.android.material.appbar.AppBarLayout>
33
34 <FrameLayout
35 android:id="@+id/frame_content"
36 android:layout_width="match_parent"
37 android:layout_height="match_parent"
38 android:layout_marginHorizontal="12dp"
39 app:layout_behavior="@string/appbar_scrolling_view_behavior" />
40 22
41 <View 23 <View
42 android:id="@+id/navigation_bar_shade" 24 android:id="@+id/navigation_bar_shade"
@@ -45,6 +27,8 @@
45 android:background="@android:color/transparent" 27 android:background="@android:color/transparent"
46 android:clickable="false" 28 android:clickable="false"
47 android:focusable="false" 29 android:focusable="false"
48 android:layout_gravity="bottom|center_horizontal" /> 30 app:layout_constraintBottom_toBottomOf="parent"
31 app:layout_constraintEnd_toEndOf="parent"
32 app:layout_constraintStart_toStartOf="parent" />
49 33
50</androidx.coordinatorlayout.widget.CoordinatorLayout> 34</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index e54a10e8f..da97d85c1 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -26,6 +26,81 @@
26 android:focusable="false" 26 android:focusable="false"
27 android:focusableInTouchMode="false" /> 27 android:focusableInTouchMode="false" />
28 28
29 <com.google.android.material.card.MaterialCardView
30 android:id="@+id/loading_indicator"
31 style="?attr/materialCardViewOutlinedStyle"
32 android:layout_width="wrap_content"
33 android:layout_height="wrap_content"
34 android:layout_gravity="center"
35 android:focusable="false">
36
37 <androidx.constraintlayout.widget.ConstraintLayout
38 android:id="@+id/loading_layout"
39 android:layout_width="wrap_content"
40 android:layout_height="wrap_content"
41 android:gravity="center_horizontal">
42
43 <ImageView
44 android:id="@+id/loading_image"
45 android:layout_width="wrap_content"
46 android:layout_height="0dp"
47 android:adjustViewBounds="true"
48 app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
49 app:layout_constraintStart_toStartOf="parent"
50 app:layout_constraintTop_toTopOf="@+id/linearLayout"
51 tools:src="@drawable/default_icon" />
52
53 <LinearLayout
54 android:id="@+id/linearLayout"
55 android:layout_width="wrap_content"
56 android:layout_height="wrap_content"
57 android:orientation="vertical"
58 android:paddingHorizontal="24dp"
59 android:paddingVertical="36dp"
60 app:layout_constraintBottom_toBottomOf="parent"
61 app:layout_constraintEnd_toEndOf="parent"
62 app:layout_constraintStart_toEndOf="@id/loading_image"
63 app:layout_constraintTop_toTopOf="parent">
64
65 <com.google.android.material.textview.MaterialTextView
66 android:id="@+id/loading_title"
67 style="@style/TextAppearance.Material3.TitleMedium"
68 android:layout_width="match_parent"
69 android:layout_height="wrap_content"
70 android:ellipsize="marquee"
71 android:marqueeRepeatLimit="marquee_forever"
72 android:requiresFadingEdge="horizontal"
73 android:singleLine="true"
74 android:textAlignment="viewStart"
75 tools:text="@string/games" />
76
77 <com.google.android.material.textview.MaterialTextView
78 android:id="@+id/loading_text"
79 style="@style/TextAppearance.Material3.TitleSmall"
80 android:layout_width="match_parent"
81 android:layout_height="wrap_content"
82 android:layout_marginTop="4dp"
83 android:ellipsize="marquee"
84 android:marqueeRepeatLimit="marquee_forever"
85 android:requiresFadingEdge="horizontal"
86 android:singleLine="true"
87 android:text="@string/loading"
88 android:textAlignment="viewStart" />
89
90 <com.google.android.material.progressindicator.LinearProgressIndicator
91 android:id="@+id/loading_progress_indicator"
92 android:layout_width="192dp"
93 android:layout_height="wrap_content"
94 android:layout_marginTop="12dp"
95 android:indeterminate="true"
96 app:trackCornerRadius="8dp" />
97
98 </LinearLayout>
99
100 </androidx.constraintlayout.widget.ConstraintLayout>
101
102 </com.google.android.material.card.MaterialCardView>
103
29 </FrameLayout> 104 </FrameLayout>
30 105
31 <FrameLayout 106 <FrameLayout
@@ -41,11 +116,12 @@
41 android:layout_height="match_parent" 116 android:layout_height="match_parent"
42 android:layout_gravity="center" 117 android:layout_gravity="center"
43 android:focusable="true" 118 android:focusable="true"
44 android:focusableInTouchMode="true" /> 119 android:focusableInTouchMode="true"
120 android:visibility="invisible" />
45 121
46 <Button 122 <Button
47 style="@style/Widget.Material3.Button.ElevatedButton"
48 android:id="@+id/done_control_config" 123 android:id="@+id/done_control_config"
124 style="@style/Widget.Material3.Button.ElevatedButton"
49 android:layout_width="wrap_content" 125 android:layout_width="wrap_content"
50 android:layout_height="wrap_content" 126 android:layout_height="wrap_content"
51 android:layout_gravity="center" 127 android:layout_gravity="center"
@@ -81,6 +157,7 @@
81 android:layout_height="match_parent" 157 android:layout_height="match_parent"
82 android:layout_gravity="start|bottom" 158 android:layout_gravity="start|bottom"
83 app:headerLayout="@layout/header_in_game" 159 app:headerLayout="@layout/header_in_game"
84 app:menu="@menu/menu_in_game" /> 160 app:menu="@menu/menu_in_game"
161 tools:visibility="gone" />
85 162
86</androidx.drawerlayout.widget.DrawerLayout> 163</androidx.drawerlayout.widget.DrawerLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml
index 167720347..ebedbf1ec 100644
--- a/src/android/app/src/main/res/layout/fragment_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_settings.xml
@@ -1,14 +1,41 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<FrameLayout 2<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 android:id="@+id/coordinator_main"
4 android:layout_width="match_parent" 5 android:layout_width="match_parent"
5 android:layout_height="match_parent"> 6 android:layout_height="match_parent"
7 android:background="?attr/colorSurface">
8
9 <com.google.android.material.appbar.AppBarLayout
10 android:id="@+id/appbar_settings"
11 android:layout_width="match_parent"
12 android:layout_height="wrap_content"
13 android:fitsSystemWindows="true"
14 app:elevation="0dp">
15
16 <com.google.android.material.appbar.CollapsingToolbarLayout
17 android:id="@+id/toolbar_settings_layout"
18 style="?attr/collapsingToolbarLayoutMediumStyle"
19 android:layout_width="match_parent"
20 android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
21 app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
22
23 <com.google.android.material.appbar.MaterialToolbar
24 android:id="@+id/toolbar_settings"
25 android:layout_width="match_parent"
26 android:layout_height="?attr/actionBarSize"
27 app:layout_collapseMode="pin"
28 app:navigationIcon="@drawable/ic_back" />
29
30 </com.google.android.material.appbar.CollapsingToolbarLayout>
31
32 </com.google.android.material.appbar.AppBarLayout>
6 33
7 <androidx.recyclerview.widget.RecyclerView 34 <androidx.recyclerview.widget.RecyclerView
8 android:id="@+id/list_settings" 35 android:id="@+id/list_settings"
9 android:layout_width="match_parent" 36 android:layout_width="match_parent"
10 android:layout_height="match_parent" 37 android:layout_height="match_parent"
11 android:background="?attr/colorSurface" 38 android:clipToPadding="false"
12 android:clipToPadding="false" /> 39 app:layout_behavior="@string/appbar_scrolling_view_behavior" />
13 40
14</FrameLayout> 41</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_settings_search.xml b/src/android/app/src/main/res/layout/fragment_settings_search.xml
new file mode 100644
index 000000000..c779ed2fc
--- /dev/null
+++ b/src/android/app/src/main/res/layout/fragment_settings_search.xml
@@ -0,0 +1,120 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent">
7
8 <RelativeLayout
9 android:id="@+id/relativeLayout"
10 android:layout_width="0dp"
11 android:layout_height="0dp"
12 app:layout_constraintBottom_toBottomOf="parent"
13 app:layout_constraintEnd_toEndOf="parent"
14 app:layout_constraintStart_toStartOf="parent"
15 app:layout_constraintTop_toBottomOf="@+id/divider">
16
17 <LinearLayout
18 android:id="@+id/no_results_view"
19 android:layout_width="match_parent"
20 android:layout_height="match_parent"
21 android:gravity="center"
22 android:orientation="vertical">
23
24 <ImageView
25 android:id="@+id/icon_no_results"
26 android:layout_width="match_parent"
27 android:layout_height="80dp"
28 android:src="@drawable/ic_search" />
29
30 <com.google.android.material.textview.MaterialTextView
31 android:id="@+id/notice_text"
32 style="@style/TextAppearance.Material3.TitleLarge"
33 android:layout_width="match_parent"
34 android:layout_height="wrap_content"
35 android:gravity="center"
36 android:paddingTop="8dp"
37 android:text="@string/search_settings"
38 tools:visibility="visible" />
39
40 </LinearLayout>
41
42 <androidx.recyclerview.widget.RecyclerView
43 android:id="@+id/settings_list"
44 android:layout_width="match_parent"
45 android:layout_height="match_parent"
46 android:clipToPadding="false" />
47
48 </RelativeLayout>
49
50 <FrameLayout
51 android:id="@+id/frame_search"
52 android:layout_width="match_parent"
53 android:layout_height="wrap_content"
54 android:clipToPadding="false"
55 app:layout_constraintEnd_toEndOf="parent"
56 app:layout_constraintStart_toStartOf="parent"
57 app:layout_constraintTop_toTopOf="parent">
58
59 <com.google.android.material.card.MaterialCardView
60 android:id="@+id/search_background"
61 style="?attr/materialCardViewFilledStyle"
62 android:layout_width="match_parent"
63 android:layout_height="56dp"
64 app:cardCornerRadius="28dp">
65
66 <LinearLayout
67 android:id="@+id/search_container"
68 android:layout_width="match_parent"
69 android:layout_height="match_parent"
70 android:layout_marginEnd="56dp"
71 android:orientation="horizontal">
72
73 <Button
74 android:id="@+id/back_button"
75 style="?attr/materialIconButtonFilledTonalStyle"
76 android:layout_width="wrap_content"
77 android:layout_height="wrap_content"
78 android:layout_gravity="center_vertical"
79 android:layout_marginStart="8dp"
80 app:backgroundTint="@android:color/transparent"
81 app:icon="@drawable/ic_back" />
82
83 <EditText
84 android:id="@+id/search_text"
85 android:layout_width="match_parent"
86 android:layout_height="match_parent"
87 android:background="@android:color/transparent"
88 android:hint="@string/search_settings"
89 android:imeOptions="flagNoFullscreen"
90 android:inputType="text"
91 android:maxLines="1" />
92
93 </LinearLayout>
94
95 <Button
96 android:id="@+id/clear_button"
97 style="?attr/materialIconButtonFilledTonalStyle"
98 android:layout_width="wrap_content"
99 android:layout_height="wrap_content"
100 android:layout_gravity="center_vertical|end"
101 android:layout_marginEnd="8dp"
102 android:visibility="invisible"
103 app:backgroundTint="@android:color/transparent"
104 app:icon="@drawable/ic_clear"
105 tools:visibility="visible" />
106
107 </com.google.android.material.card.MaterialCardView>
108
109 </FrameLayout>
110
111 <com.google.android.material.divider.MaterialDivider
112 android:id="@+id/divider"
113 android:layout_width="match_parent"
114 android:layout_height="wrap_content"
115 android:layout_marginTop="20dp"
116 app:layout_constraintEnd_toEndOf="parent"
117 app:layout_constraintStart_toStartOf="parent"
118 app:layout_constraintTop_toBottomOf="@+id/frame_search" />
119
120</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/src/android/app/src/main/res/menu/menu_settings.xml b/src/android/app/src/main/res/menu/menu_settings.xml
index 1fe7aa6d4..21501a471 100644
--- a/src/android/app/src/main/res/menu/menu_settings.xml
+++ b/src/android/app/src/main/res/menu/menu_settings.xml
@@ -1,2 +1,11 @@
1<?xml version="1.0" encoding="utf-8"?> 1<?xml version="1.0" encoding="utf-8"?>
2<menu /> \ No newline at end of file 2<menu xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto">
4
5 <item
6 android:id="@+id/action_search"
7 android:icon="@drawable/ic_search"
8 android:title="@string/home_search"
9 app:showAsAction="always" />
10
11</menu>
diff --git a/src/android/app/src/main/res/navigation/emulation_navigation.xml b/src/android/app/src/main/res/navigation/emulation_navigation.xml
index 8208f4c2c..c7be37f9b 100644
--- a/src/android/app/src/main/res/navigation/emulation_navigation.xml
+++ b/src/android/app/src/main/res/navigation/emulation_navigation.xml
@@ -12,7 +12,26 @@
12 tools:layout="@layout/fragment_emulation" > 12 tools:layout="@layout/fragment_emulation" >
13 <argument 13 <argument
14 android:name="game" 14 android:name="game"
15 app:argType="org.yuzu.yuzu_emu.model.Game" /> 15 app:argType="org.yuzu.yuzu_emu.model.Game"
16 app:nullable="true"
17 android:defaultValue="@null" />
16 </fragment> 18 </fragment>
17 19
20 <activity
21 android:id="@+id/settingsActivity"
22 android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity"
23 android:label="SettingsActivity">
24 <argument
25 android:name="game"
26 app:argType="org.yuzu.yuzu_emu.model.Game"
27 app:nullable="true" />
28 <argument
29 android:name="menuTag"
30 app:argType="string" />
31 </activity>
32
33 <action
34 android:id="@+id/action_global_settingsActivity"
35 app:destination="@id/settingsActivity" />
36
18</navigation> 37</navigation>
diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml
index fcebba726..2085430bf 100644
--- a/src/android/app/src/main/res/navigation/home_navigation.xml
+++ b/src/android/app/src/main/res/navigation/home_navigation.xml
@@ -62,7 +62,9 @@
62 android:label="EmulationActivity"> 62 android:label="EmulationActivity">
63 <argument 63 <argument
64 android:name="game" 64 android:name="game"
65 app:argType="org.yuzu.yuzu_emu.model.Game" /> 65 app:argType="org.yuzu.yuzu_emu.model.Game"
66 app:nullable="true"
67 android:defaultValue="@null" />
66 </activity> 68 </activity>
67 69
68 <action 70 <action
@@ -70,4 +72,21 @@
70 app:destination="@id/emulationActivity" 72 app:destination="@id/emulationActivity"
71 app:launchSingleTop="true" /> 73 app:launchSingleTop="true" />
72 74
75 <activity
76 android:id="@+id/settingsActivity"
77 android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity"
78 android:label="SettingsActivity">
79 <argument
80 android:name="game"
81 app:argType="org.yuzu.yuzu_emu.model.Game"
82 app:nullable="true" />
83 <argument
84 android:name="menuTag"
85 app:argType="string" />
86 </activity>
87
88 <action
89 android:id="@+id/action_global_settingsActivity"
90 app:destination="@id/settingsActivity" />
91
73</navigation> 92</navigation>
diff --git a/src/android/app/src/main/res/navigation/settings_navigation.xml b/src/android/app/src/main/res/navigation/settings_navigation.xml
new file mode 100644
index 000000000..88e1b4587
--- /dev/null
+++ b/src/android/app/src/main/res/navigation/settings_navigation.xml
@@ -0,0 +1,32 @@
1<?xml version="1.0" encoding="utf-8"?>
2<navigation xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 android:id="@+id/settings_navigation"
5 app:startDestination="@id/settingsFragment">
6
7 <fragment
8 android:id="@+id/settingsFragment"
9 android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsFragment"
10 android:label="SettingsFragment">
11 <argument
12 android:name="menuTag"
13 app:argType="string" />
14 <argument
15 android:name="game"
16 app:argType="org.yuzu.yuzu_emu.model.Game"
17 app:nullable="true" />
18 <action
19 android:id="@+id/action_settingsFragment_to_settingsSearchFragment"
20 app:destination="@id/settingsSearchFragment" />
21 </fragment>
22
23 <action
24 android:id="@+id/action_global_settingsFragment"
25 app:destination="@id/settingsFragment" />
26
27 <fragment
28 android:id="@+id/settingsSearchFragment"
29 android:name="org.yuzu.yuzu_emu.fragments.SettingsSearchFragment"
30 android:label="SettingsSearchFragment" />
31
32</navigation>
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
index 0c1d91264..daaa7ffde 100644
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -209,7 +209,6 @@
209 <string name="emulation_pause">Emulation pausieren</string> 209 <string name="emulation_pause">Emulation pausieren</string>
210 <string name="emulation_unpause">Emulation fortsetzen</string> 210 <string name="emulation_unpause">Emulation fortsetzen</string>
211 <string name="emulation_input_overlay">Overlay-Optionen</string> 211 <string name="emulation_input_overlay">Overlay-Optionen</string>
212 <string name="emulation_game_loading">Spiel lädt…</string>
213 212
214 <string name="load_settings">Lädt Einstellungen...</string> 213 <string name="load_settings">Lädt Einstellungen...</string>
215 214
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index 357f956d1..e9129cb00 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pausar Emulación</string> 213 <string name="emulation_pause">Pausar Emulación</string>
214 <string name="emulation_unpause">Reanudar Emulación</string> 214 <string name="emulation_unpause">Reanudar Emulación</string>
215 <string name="emulation_input_overlay">Opciones de pantalla </string> 215 <string name="emulation_input_overlay">Opciones de pantalla </string>
216 <string name="emulation_game_loading">Cargando juego...</string>
217 216
218 <string name="load_settings">Cargando configuración...</string> 217 <string name="load_settings">Cargando configuración...</string>
219 218
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml
index dfca1c830..2d99d618e 100644
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Mettre en pause l\'émulation</string> 213 <string name="emulation_pause">Mettre en pause l\'émulation</string>
214 <string name="emulation_unpause">Reprendre l\'émulation</string> 214 <string name="emulation_unpause">Reprendre l\'émulation</string>
215 <string name="emulation_input_overlay">Options de l\'overlay</string> 215 <string name="emulation_input_overlay">Options de l\'overlay</string>
216 <string name="emulation_game_loading">Chargement du jeu...</string>
217 216
218 <string name="load_settings">Chargement des paramètres…</string> 217 <string name="load_settings">Chargement des paramètres…</string>
219 218
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml
index 089d93ed6..d9c3de385 100644
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Metti in pausa l\'emulazione</string> 213 <string name="emulation_pause">Metti in pausa l\'emulazione</string>
214 <string name="emulation_unpause">Riprendi Emulazione</string> 214 <string name="emulation_unpause">Riprendi Emulazione</string>
215 <string name="emulation_input_overlay">Impostazioni Overlay</string> 215 <string name="emulation_input_overlay">Impostazioni Overlay</string>
216 <string name="emulation_game_loading">Caricamento del gioco...</string>
217 216
218 <string name="load_settings">Caricamento delle impostazioni...</string> 217 <string name="load_settings">Caricamento delle impostazioni...</string>
219 218
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml
index 39b590bee..7a226cd5c 100644
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -211,7 +211,6 @@
211 <string name="emulation_pause">エミュレーションを一時停止</string> 211 <string name="emulation_pause">エミュレーションを一時停止</string>
212 <string name="emulation_unpause">エミュレーションを再開</string> 212 <string name="emulation_unpause">エミュレーションを再開</string>
213 <string name="emulation_input_overlay">オーバーレイオプション</string> 213 <string name="emulation_input_overlay">オーバーレイオプション</string>
214 <string name="emulation_game_loading">ロード中…</string>
215 214
216 <string name="load_settings">設定をロード中…</string> 215 <string name="load_settings">設定をロード中…</string>
217 216
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml
index cbcb2873f..427b6e5a0 100644
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">에뮬레이션 일시 중지</string> 213 <string name="emulation_pause">에뮬레이션 일시 중지</string>
214 <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string> 214 <string name="emulation_unpause">에뮬레이션 일시 중지 해제</string>
215 <string name="emulation_input_overlay">오버레이 옵션</string> 215 <string name="emulation_input_overlay">오버레이 옵션</string>
216 <string name="emulation_game_loading">게임 불러오기 중...</string>
217 216
218 <string name="load_settings">설정 불러오기 중...</string> 217 <string name="load_settings">설정 불러오기 중...</string>
219 218
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml
index e48a4be38..ce8d7a9e4 100644
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pause Emulering</string> 213 <string name="emulation_pause">Pause Emulering</string>
214 <string name="emulation_unpause">Opphev pausing av emulering</string> 214 <string name="emulation_unpause">Opphev pausing av emulering</string>
215 <string name="emulation_input_overlay">Alternativer for overlegg</string> 215 <string name="emulation_input_overlay">Alternativer for overlegg</string>
216 <string name="emulation_game_loading">Spillet lastes inn...</string>
217 216
218 <string name="load_settings">Laster inn innstillinger...</string> 217 <string name="load_settings">Laster inn innstillinger...</string>
219 218
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml
index bc9c0f7f4..c2c24b48f 100644
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Wstrzymaj emulację</string> 213 <string name="emulation_pause">Wstrzymaj emulację</string>
214 <string name="emulation_unpause">Wznów emulację</string> 214 <string name="emulation_unpause">Wznów emulację</string>
215 <string name="emulation_input_overlay">Opcje nakładki</string> 215 <string name="emulation_input_overlay">Opcje nakładki</string>
216 <string name="emulation_game_loading">Wczytywanie gry...</string>
217 216
218 <string name="load_settings">Wczytywanie ustawień...</string> 217 <string name="load_settings">Wczytywanie ustawień...</string>
219 218
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml
index 75fe0edbf..04f276108 100644
--- a/src/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pausa emulação</string> 213 <string name="emulation_pause">Pausa emulação</string>
214 <string name="emulation_unpause">Retomar emulação</string> 214 <string name="emulation_unpause">Retomar emulação</string>
215 <string name="emulation_input_overlay">Opções de sobreposição </string> 215 <string name="emulation_input_overlay">Opções de sobreposição </string>
216 <string name="emulation_game_loading">Jogo a carregar...</string>
217 216
218 <string name="load_settings">Configurações a carregar...</string> 217 <string name="load_settings">Configurações a carregar...</string>
219 218
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml
index 96b040c66..66a3a1a2e 100644
--- a/src/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Pausa emulação</string> 213 <string name="emulation_pause">Pausa emulação</string>
214 <string name="emulation_unpause">Retomar emulação</string> 214 <string name="emulation_unpause">Retomar emulação</string>
215 <string name="emulation_input_overlay">Opções de sobreposição </string> 215 <string name="emulation_input_overlay">Opções de sobreposição </string>
216 <string name="emulation_game_loading">Jogo a carregar...</string>
217 216
218 <string name="load_settings">Configurações a carregar...</string> 217 <string name="load_settings">Configurações a carregar...</string>
219 218
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml
index 8d954f59e..f770e954f 100644
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Пауза эмуляции</string> 213 <string name="emulation_pause">Пауза эмуляции</string>
214 <string name="emulation_unpause">Возобновление эмуляции</string> 214 <string name="emulation_unpause">Возобновление эмуляции</string>
215 <string name="emulation_input_overlay">Настройки оверлея</string> 215 <string name="emulation_input_overlay">Настройки оверлея</string>
216 <string name="emulation_game_loading">Загрузка игры...</string>
217 216
218 <string name="load_settings">Загрузка настроек...</string> 217 <string name="load_settings">Загрузка настроек...</string>
219 218
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml
index 6c028535b..ea3ab1b15 100644
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">Пауза емуляції</string> 213 <string name="emulation_pause">Пауза емуляції</string>
214 <string name="emulation_unpause">Відновлення емуляції</string> 214 <string name="emulation_unpause">Відновлення емуляції</string>
215 <string name="emulation_input_overlay">Налаштування оверлея</string> 215 <string name="emulation_input_overlay">Налаштування оверлея</string>
216 <string name="emulation_game_loading">Завантаження гри...</string>
217 216
218 <string name="load_settings">Завантаження налаштувань...</string> 217 <string name="load_settings">Завантаження налаштувань...</string>
219 218
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml
index e4ad2ed07..b45a5a528 100644
--- a/src/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">暂停模拟</string> 213 <string name="emulation_pause">暂停模拟</string>
214 <string name="emulation_unpause">继续模拟</string> 214 <string name="emulation_unpause">继续模拟</string>
215 <string name="emulation_input_overlay">虚拟按键选项</string> 215 <string name="emulation_input_overlay">虚拟按键选项</string>
216 <string name="emulation_game_loading">载入游戏中…</string>
217 216
218 <string name="load_settings">正在载入设定…</string> 217 <string name="load_settings">正在载入设定…</string>
219 218
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml
index 0d32f23df..3aab889e4 100644
--- a/src/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -213,7 +213,6 @@
213 <string name="emulation_pause">暫停模擬</string> 213 <string name="emulation_pause">暫停模擬</string>
214 <string name="emulation_unpause">取消暫停模擬</string> 214 <string name="emulation_unpause">取消暫停模擬</string>
215 <string name="emulation_input_overlay">覆疊選項</string> 215 <string name="emulation_input_overlay">覆疊選項</string>
216 <string name="emulation_game_loading">遊戲正在載入…</string>
217 216
218 <string name="load_settings">正在載入設定…</string> 217 <string name="load_settings">正在載入設定…</string>
219 218
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 200b99185..dc10159c9 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -243,10 +243,10 @@
243 <item>@string/cubeb</item> 243 <item>@string/cubeb</item>
244 <item>@string/string_null</item> 244 <item>@string/string_null</item>
245 </string-array> 245 </string-array>
246 <string-array name="outputEngineValues"> 246 <integer-array name="outputEngineValues">
247 <item>auto</item> 247 <item>0</item>
248 <item>cubeb</item> 248 <item>1</item>
249 <item>null</item> 249 <item>3</item>
250 </string-array> 250 </integer-array>
251 251
252</resources> 252</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index de1b2909b..b163e6fc1 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -43,6 +43,7 @@
43 <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string> 43 <string name="add_games_warning_description">Games won\'t be displayed in the Games list if a folder isn\'t selected.</string>
44 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string> 44 <string name="add_games_warning_help">https://yuzu-emu.org/help/quickstart/#dumping-games</string>
45 <string name="home_search_games">Search games</string> 45 <string name="home_search_games">Search games</string>
46 <string name="search_settings">Search settings</string>
46 <string name="games_dir_selected">Games directory selected</string> 47 <string name="games_dir_selected">Games directory selected</string>
47 <string name="install_prod_keys">Install prod.keys</string> 48 <string name="install_prod_keys">Install prod.keys</string>
48 <string name="install_prod_keys_description">Required to decrypt retail games</string> 49 <string name="install_prod_keys_description">Required to decrypt retail games</string>
@@ -74,6 +75,7 @@
74 <string name="install_gpu_driver">Install GPU driver</string> 75 <string name="install_gpu_driver">Install GPU driver</string>
75 <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> 76 <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
76 <string name="advanced_settings">Advanced settings</string> 77 <string name="advanced_settings">Advanced settings</string>
78 <string name="advanced_settings_game">Advanced settings: %1$s</string>
77 <string name="settings_description">Configure emulator settings</string> 79 <string name="settings_description">Configure emulator settings</string>
78 <string name="search_recently_played">Recently played</string> 80 <string name="search_recently_played">Recently played</string>
79 <string name="search_recently_added">Recently added</string> 81 <string name="search_recently_added">Recently added</string>
@@ -200,7 +202,9 @@
200 <string name="ini_saved">Saved settings</string> 202 <string name="ini_saved">Saved settings</string>
201 <string name="gameid_saved">Saved settings for %1$s</string> 203 <string name="gameid_saved">Saved settings for %1$s</string>
202 <string name="error_saving">Error saving %1$s.ini: %2$s</string> 204 <string name="error_saving">Error saving %1$s.ini: %2$s</string>
205 <string name="unimplemented_menu">Unimplemented Menu</string>
203 <string name="loading">Loading…</string> 206 <string name="loading">Loading…</string>
207 <string name="shutting_down">Shutting down…</string>
204 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> 208 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
205 <string name="reset_to_default">Reset to default</string> 209 <string name="reset_to_default">Reset to default</string>
206 <string name="reset_all_settings">Reset all settings?</string> 210 <string name="reset_all_settings">Reset all settings?</string>
@@ -259,7 +263,6 @@
259 <string name="emulation_pause">Pause emulation</string> 263 <string name="emulation_pause">Pause emulation</string>
260 <string name="emulation_unpause">Unpause emulation</string> 264 <string name="emulation_unpause">Unpause emulation</string>
261 <string name="emulation_input_overlay">Overlay options</string> 265 <string name="emulation_input_overlay">Overlay options</string>
262 <string name="emulation_game_loading">Game loading…</string>
263 266
264 <string name="load_settings">Loading settings…</string> 267 <string name="load_settings">Loading settings…</string>
265 268
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index e7b595459..67dfe0290 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -2,6 +2,14 @@
2# SPDX-License-Identifier: GPL-2.0-or-later 2# SPDX-License-Identifier: GPL-2.0-or-later
3 3
4add_library(audio_core STATIC 4add_library(audio_core STATIC
5 adsp/adsp.cpp
6 adsp/adsp.h
7 adsp/mailbox.h
8 adsp/apps/audio_renderer/audio_renderer.cpp
9 adsp/apps/audio_renderer/audio_renderer.h
10 adsp/apps/audio_renderer/command_buffer.h
11 adsp/apps/audio_renderer/command_list_processor.cpp
12 adsp/apps/audio_renderer/command_list_processor.h
5 audio_core.cpp 13 audio_core.cpp
6 audio_core.h 14 audio_core.h
7 audio_event.h 15 audio_event.h
@@ -32,13 +40,6 @@ add_library(audio_core STATIC
32 out/audio_out_system.cpp 40 out/audio_out_system.cpp
33 out/audio_out_system.h 41 out/audio_out_system.h
34 precompiled_headers.h 42 precompiled_headers.h
35 renderer/adsp/adsp.cpp
36 renderer/adsp/adsp.h
37 renderer/adsp/audio_renderer.cpp
38 renderer/adsp/audio_renderer.h
39 renderer/adsp/command_buffer.h
40 renderer/adsp/command_list_processor.cpp
41 renderer/adsp/command_list_processor.h
42 renderer/audio_device.cpp 43 renderer/audio_device.cpp
43 renderer/audio_device.h 44 renderer/audio_device.h
44 renderer/audio_renderer.h 45 renderer/audio_renderer.h
diff --git a/src/audio_core/adsp/adsp.cpp b/src/audio_core/adsp/adsp.cpp
new file mode 100644
index 000000000..0580990f5
--- /dev/null
+++ b/src/audio_core/adsp/adsp.cpp
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/adsp/adsp.h"
5#include "core/core.h"
6
7namespace AudioCore::ADSP {
8
9ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
10 audio_renderer =
11 std::make_unique<AudioRenderer::AudioRenderer>(system, system.ApplicationMemory(), sink);
12}
13
14AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
15 return *audio_renderer.get();
16}
17
18} // namespace AudioCore::ADSP
diff --git a/src/audio_core/adsp/adsp.h b/src/audio_core/adsp/adsp.h
new file mode 100644
index 000000000..bd5bcc63b
--- /dev/null
+++ b/src/audio_core/adsp/adsp.h
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h"
7#include "common/common_types.h"
8
9namespace Core {
10class System;
11} // namespace Core
12
13namespace AudioCore {
14namespace Sink {
15class Sink;
16}
17
18namespace ADSP {
19
20/**
21 * Represents the ADSP embedded within the audio sysmodule.
22 * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot.
23 *
24 * The kernel will run the apps you write for it, Nintendo have the following:
25 *
26 * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all
27 * audio samples end up, and we skip it entirely, since we have very different backends and
28 * mixing is implicitly handled by the OS (but also due to lack of research/simplicity).
29 *
30 * AudioRenderer - Receives command lists generated by the audio render
31 * system on the host, processes them, and sends the samples to Gmix.
32 *
33 * OpusDecoder - Contains libopus, and decodes Opus audio packets into raw pcm data.
34 *
35 * Communication between the host and ADSP is done through mailboxes, and mapping of shared memory.
36 */
37class ADSP {
38public:
39 explicit ADSP(Core::System& system, Sink::Sink& sink);
40 ~ADSP() = default;
41
42 AudioRenderer::AudioRenderer& AudioRenderer();
43
44private:
45 /// AudioRenderer app
46 std::unique_ptr<AudioRenderer::AudioRenderer> audio_renderer{};
47};
48
49} // namespace ADSP
50} // namespace AudioCore
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
new file mode 100644
index 000000000..2e549bc6f
--- /dev/null
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.cpp
@@ -0,0 +1,220 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5#include <chrono>
6
7#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h"
8#include "audio_core/audio_core.h"
9#include "audio_core/common/common.h"
10#include "audio_core/sink/sink.h"
11#include "common/logging/log.h"
12#include "common/microprofile.h"
13#include "common/thread.h"
14#include "core/core.h"
15#include "core/core_timing.h"
16
17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
18
19namespace AudioCore::ADSP::AudioRenderer {
20
21AudioRenderer::AudioRenderer(Core::System& system_, Core::Memory::Memory& memory_,
22 Sink::Sink& sink_)
23 : system{system_}, memory{memory_}, sink{sink_} {}
24
25AudioRenderer::~AudioRenderer() {
26 Stop();
27}
28
29void AudioRenderer::Start() {
30 CreateSinkStreams();
31
32 mailbox.Initialize(AppMailboxId::AudioRenderer);
33
34 main_thread = std::jthread([this](std::stop_token stop_token) { Main(stop_token); });
35
36 mailbox.Send(Direction::DSP, {Message::InitializeOK, {}});
37 if (mailbox.Receive(Direction::Host).msg != Message::InitializeOK) {
38 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
39 "message response from ADSP!");
40 return;
41 }
42 running = true;
43}
44
45void AudioRenderer::Stop() {
46 if (!running) {
47 return;
48 }
49
50 mailbox.Send(Direction::DSP, {Message::Shutdown, {}});
51 if (mailbox.Receive(Direction::Host).msg != Message::Shutdown) {
52 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
53 "message response from ADSP!");
54 }
55 main_thread.request_stop();
56 main_thread.join();
57
58 for (auto& stream : streams) {
59 if (stream) {
60 stream->Stop();
61 sink.CloseStream(stream);
62 stream = nullptr;
63 }
64 }
65 running = false;
66}
67
68void AudioRenderer::Signal() {
69 signalled_tick = system.CoreTiming().GetGlobalTimeNs().count();
70 Send(Direction::DSP, {Message::Render, {}});
71}
72
73void AudioRenderer::Wait() {
74 auto received = Receive(Direction::Host);
75 if (received.msg != Message::RenderResponse) {
76 LOG_ERROR(Service_Audio,
77 "Did not receive the expected render response from the AudioRenderer! Expected "
78 "{}, got {}",
79 Message::RenderResponse, received.msg);
80 }
81}
82
83void AudioRenderer::Send(Direction dir, MailboxMessage message) {
84 mailbox.Send(dir, std::move(message));
85}
86
87MailboxMessage AudioRenderer::Receive(Direction dir, bool block) {
88 return mailbox.Receive(dir, block);
89}
90
91void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit,
92 u64 applet_resource_user_id, bool reset) noexcept {
93 command_buffers[session_id].buffer = buffer;
94 command_buffers[session_id].size = size;
95 command_buffers[session_id].time_limit = time_limit;
96 command_buffers[session_id].applet_resource_user_id = applet_resource_user_id;
97 command_buffers[session_id].reset_buffer = reset;
98}
99
100u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
101 return command_buffers[session_id].remaining_command_count;
102}
103
104void AudioRenderer::ClearRemainCommandCount(s32 session_id) noexcept {
105 command_buffers[session_id].remaining_command_count = 0;
106}
107
108u64 AudioRenderer::GetRenderingStartTick(s32 session_id) const noexcept {
109 return (1000 * command_buffers[session_id].render_time_taken_us) + signalled_tick;
110}
111
112void AudioRenderer::CreateSinkStreams() {
113 u32 channels{sink.GetDeviceChannels()};
114 for (u32 i = 0; i < MaxRendererSessions; i++) {
115 std::string name{fmt::format("ADSP_RenderStream-{}", i)};
116 streams[i] =
117 sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render);
118 streams[i]->SetRingSize(4);
119 }
120}
121
122void AudioRenderer::Main(std::stop_token stop_token) {
123 static constexpr char name[]{"AudioRenderer"};
124 MicroProfileOnThreadCreate(name);
125 Common::SetCurrentThreadName(name);
126 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
127
128 // TODO: Create buffer map/unmap thread + mailbox
129 // TODO: Create gMix devices, initialize them here
130
131 if (mailbox.Receive(Direction::DSP).msg != Message::InitializeOK) {
132 LOG_ERROR(Service_Audio,
133 "ADSP Audio Renderer -- Failed to receive initialize message from host!");
134 return;
135 }
136
137 mailbox.Send(Direction::Host, {Message::InitializeOK, {}});
138
139 // 0.12 seconds (2,304,000 / 19,200,000)
140 constexpr u64 max_process_time{2'304'000ULL};
141
142 while (!stop_token.stop_requested()) {
143 auto received{mailbox.Receive(Direction::DSP)};
144 switch (received.msg) {
145 case Message::Shutdown:
146 mailbox.Send(Direction::Host, {Message::Shutdown, {}});
147 return;
148
149 case Message::Render: {
150 if (system.IsShuttingDown()) [[unlikely]] {
151 std::this_thread::sleep_for(std::chrono::milliseconds(5));
152 mailbox.Send(Direction::Host, {Message::RenderResponse, {}});
153 continue;
154 }
155 std::array<bool, MaxRendererSessions> buffers_reset{};
156 std::array<u64, MaxRendererSessions> render_times_taken{};
157 const auto start_time{system.CoreTiming().GetGlobalTimeUs().count()};
158
159 for (u32 index = 0; index < MaxRendererSessions; index++) {
160 auto& command_buffer{command_buffers[index]};
161 auto& command_list_processor{command_list_processors[index]};
162
163 // Check this buffer is valid, as it may not be used.
164 if (command_buffer.buffer != 0) {
165 // If there are no remaining commands (from the previous list),
166 // this is a new command list, initialize it.
167 if (command_buffer.remaining_command_count == 0) {
168 command_list_processor.Initialize(system, command_buffer.buffer,
169 command_buffer.size, streams[index]);
170 }
171
172 if (command_buffer.reset_buffer && !buffers_reset[index]) {
173 streams[index]->ClearQueue();
174 buffers_reset[index] = true;
175 }
176
177 u64 max_time{max_process_time};
178 if (index == 1 && command_buffer.applet_resource_user_id ==
179 command_buffers[0].applet_resource_user_id) {
180 max_time = max_process_time - render_times_taken[0];
181 if (render_times_taken[0] > max_process_time) {
182 max_time = 0;
183 }
184 }
185
186 max_time = std::min(command_buffer.time_limit, max_time);
187 command_list_processor.SetProcessTimeMax(max_time);
188
189 if (index == 0) {
190 streams[index]->WaitFreeSpace(stop_token);
191 }
192
193 // Process the command list
194 {
195 MICROPROFILE_SCOPE(Audio_Renderer);
196 render_times_taken[index] =
197 command_list_processor.Process(index) - start_time;
198 }
199
200 const auto end_time{system.CoreTiming().GetGlobalTimeUs().count()};
201
202 command_buffer.remaining_command_count =
203 command_list_processor.GetRemainingCommandCount();
204 command_buffer.render_time_taken_us = end_time - start_time;
205 }
206 }
207
208 mailbox.Send(Direction::Host, {Message::RenderResponse, {}});
209 } break;
210
211 default:
212 LOG_WARNING(Service_Audio,
213 "ADSP AudioRenderer received an invalid message, msg={:02X}!",
214 received.msg);
215 break;
216 }
217 }
218}
219
220} // namespace AudioCore::ADSP::AudioRenderer
diff --git a/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
new file mode 100644
index 000000000..3f5b7dca2
--- /dev/null
+++ b/src/audio_core/adsp/apps/audio_renderer/audio_renderer.h
@@ -0,0 +1,116 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <thread>
9
10#include "audio_core/adsp/apps/audio_renderer/command_buffer.h"
11#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
12#include "audio_core/adsp/mailbox.h"
13#include "common/common_types.h"
14#include "common/polyfill_thread.h"
15#include "common/reader_writer_queue.h"
16#include "common/thread.h"
17
18namespace Core {
19class System;
20namespace Timing {
21struct EventType;
22}
23namespace Memory {
24class Memory;
25}
26class System;
27} // namespace Core
28
29namespace AudioCore {
30namespace Sink {
31class Sink;
32}
33
34namespace ADSP::AudioRenderer {
35
36enum Message : u32 {
37 Invalid = 0x00,
38 MapUnmap_Map = 0x01,
39 MapUnmap_MapResponse = 0x02,
40 MapUnmap_Unmap = 0x03,
41 MapUnmap_UnmapResponse = 0x04,
42 MapUnmap_InvalidateCache = 0x05,
43 MapUnmap_InvalidateCacheResponse = 0x06,
44 MapUnmap_Shutdown = 0x07,
45 MapUnmap_ShutdownResponse = 0x08,
46 InitializeOK = 0x16,
47 RenderResponse = 0x20,
48 Render = 0x2A,
49 Shutdown = 0x34,
50};
51
52/**
53 * The AudioRenderer application running on the ADSP.
54 */
55class AudioRenderer {
56public:
57 explicit AudioRenderer(Core::System& system, Core::Memory::Memory& memory, Sink::Sink& sink);
58 ~AudioRenderer();
59
60 /**
61 * Start the AudioRenderer.
62 *
63 * @param mailbox The mailbox to use for this session.
64 */
65 void Start();
66
67 /**
68 * Stop the AudioRenderer.
69 */
70 void Stop();
71
72 void Signal();
73 void Wait();
74
75 void Send(Direction dir, MailboxMessage message);
76 MailboxMessage Receive(Direction dir, bool block = true);
77
78 void SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u64 time_limit,
79 u64 applet_resource_user_id, bool reset) noexcept;
80 u32 GetRemainCommandCount(s32 session_id) const noexcept;
81 void ClearRemainCommandCount(s32 session_id) noexcept;
82 u64 GetRenderingStartTick(s32 session_id) const noexcept;
83
84private:
85 /**
86 * Main AudioRenderer thread, responsible for processing the command lists.
87 */
88 void Main(std::stop_token stop_token);
89
90 /**
91 * Creates the streams which will receive the processed samples.
92 */
93 void CreateSinkStreams();
94
95 /// Core system
96 Core::System& system;
97 /// Memory
98 Core::Memory::Memory& memory;
99 /// The output sink the AudioRenderer will use
100 Sink::Sink& sink;
101 /// The active mailbox
102 Mailbox mailbox;
103 /// Main thread
104 std::jthread main_thread{};
105 /// The current state
106 std::atomic<bool> running{};
107 std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
108 /// The command lists to process
109 std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{};
110 /// The streams which will receive the processed samples
111 std::array<Sink::SinkStream*, MaxRendererSessions> streams{};
112 u64 signalled_tick{0};
113};
114
115} // namespace ADSP::AudioRenderer
116} // namespace AudioCore
diff --git a/src/audio_core/adsp/apps/audio_renderer/command_buffer.h b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h
new file mode 100644
index 000000000..3fd1b09dc
--- /dev/null
+++ b/src/audio_core/adsp/apps/audio_renderer/command_buffer.h
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/common/common.h"
7#include "common/common_types.h"
8
9namespace AudioCore::ADSP::AudioRenderer {
10
11struct CommandBuffer {
12 // Set by the host
13 CpuAddr buffer{};
14 u64 size{};
15 u64 time_limit{};
16 u64 applet_resource_user_id{};
17 bool reset_buffer{};
18 // Set by the DSP
19 u32 remaining_command_count{};
20 u64 render_time_taken_us{};
21};
22
23} // namespace AudioCore::ADSP::AudioRenderer
diff --git a/src/audio_core/renderer/adsp/command_list_processor.cpp b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp
index 3a0f1ae38..24e4d0496 100644
--- a/src/audio_core/renderer/adsp/command_list_processor.cpp
+++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.cpp
@@ -1,9 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2022 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 <string> 4#include <string>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/command_list_header.h" 7#include "audio_core/renderer/command/command_list_header.h"
8#include "audio_core/renderer/command/commands.h" 8#include "audio_core/renderer/command/commands.h"
9#include "common/settings.h" 9#include "common/settings.h"
@@ -11,15 +11,15 @@
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/memory.h" 12#include "core/memory.h"
13 13
14namespace AudioCore::AudioRenderer::ADSP { 14namespace AudioCore::ADSP::AudioRenderer {
15 15
16void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size, 16void CommandListProcessor::Initialize(Core::System& system_, CpuAddr buffer, u64 size,
17 Sink::SinkStream* stream_) { 17 Sink::SinkStream* stream_) {
18 system = &system_; 18 system = &system_;
19 memory = &system->ApplicationMemory(); 19 memory = &system->ApplicationMemory();
20 stream = stream_; 20 stream = stream_;
21 header = reinterpret_cast<CommandListHeader*>(buffer); 21 header = reinterpret_cast<Renderer::CommandListHeader*>(buffer);
22 commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader)); 22 commands = reinterpret_cast<u8*>(buffer + sizeof(Renderer::CommandListHeader));
23 commands_buffer_size = size; 23 commands_buffer_size = size;
24 command_count = header->command_count; 24 command_count = header->command_count;
25 sample_count = header->sample_count; 25 sample_count = header->sample_count;
@@ -37,17 +37,12 @@ u32 CommandListProcessor::GetRemainingCommandCount() const {
37 return command_count - processed_command_count; 37 return command_count - processed_command_count;
38} 38}
39 39
40void CommandListProcessor::SetBuffer(const CpuAddr buffer, const u64 size) {
41 commands = reinterpret_cast<u8*>(buffer + sizeof(CommandListHeader));
42 commands_buffer_size = size;
43}
44
45Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const { 40Sink::SinkStream* CommandListProcessor::GetOutputSinkStream() const {
46 return stream; 41 return stream;
47} 42}
48 43
49u64 CommandListProcessor::Process(u32 session_id) { 44u64 CommandListProcessor::Process(u32 session_id) {
50 const auto start_time_{system->CoreTiming().GetClockTicks()}; 45 const auto start_time_{system->CoreTiming().GetGlobalTimeUs().count()};
51 const auto command_base{CpuAddr(commands)}; 46 const auto command_base{CpuAddr(commands)};
52 47
53 if (processed_command_count > 0) { 48 if (processed_command_count > 0) {
@@ -60,12 +55,12 @@ u64 CommandListProcessor::Process(u32 session_id) {
60 std::string dump{fmt::format("\nSession {}\n", session_id)}; 55 std::string dump{fmt::format("\nSession {}\n", session_id)};
61 56
62 for (u32 index = 0; index < command_count; index++) { 57 for (u32 index = 0; index < command_count; index++) {
63 auto& command{*reinterpret_cast<ICommand*>(commands)}; 58 auto& command{*reinterpret_cast<Renderer::ICommand*>(commands)};
64 59
65 if (command.magic != 0xCAFEBABE) { 60 if (command.magic != 0xCAFEBABE) {
66 LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}", 61 LOG_ERROR(Service_Audio, "Command has invalid magic! Expected 0xCAFEBABE, got {:08X}",
67 command.magic); 62 command.magic);
68 return system->CoreTiming().GetClockTicks() - start_time_; 63 return system->CoreTiming().GetGlobalTimeUs().count() - start_time_;
69 } 64 }
70 65
71 auto current_offset{CpuAddr(commands) - command_base}; 66 auto current_offset{CpuAddr(commands) - command_base};
@@ -74,8 +69,8 @@ u64 CommandListProcessor::Process(u32 session_id) {
74 LOG_ERROR(Service_Audio, 69 LOG_ERROR(Service_Audio,
75 "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}", 70 "Command exceeded command buffer, buffer size {:08X}, command ends at {:08X}",
76 commands_buffer_size, 71 commands_buffer_size,
77 CpuAddr(commands) + command.size - sizeof(CommandListHeader)); 72 CpuAddr(commands) + command.size - sizeof(Renderer::CommandListHeader));
78 return system->CoreTiming().GetClockTicks() - start_time_; 73 return system->CoreTiming().GetGlobalTimeUs().count() - start_time_;
79 } 74 }
80 75
81 if (Settings::values.dump_audio_commands) { 76 if (Settings::values.dump_audio_commands) {
@@ -101,8 +96,8 @@ u64 CommandListProcessor::Process(u32 session_id) {
101 last_dump = dump; 96 last_dump = dump;
102 } 97 }
103 98
104 end_time = system->CoreTiming().GetClockTicks(); 99 end_time = system->CoreTiming().GetGlobalTimeUs().count();
105 return end_time - start_time_; 100 return end_time - start_time_;
106} 101}
107 102
108} // namespace AudioCore::AudioRenderer::ADSP 103} // namespace AudioCore::ADSP::AudioRenderer
diff --git a/src/audio_core/renderer/adsp/command_list_processor.h b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h
index d78269e1d..4e5fb793e 100644
--- a/src/audio_core/renderer/adsp/command_list_processor.h
+++ b/src/audio_core/adsp/apps/audio_renderer/command_list_processor.h
@@ -1,4 +1,4 @@
1// SPDX-FileCopyrightText: Copyright 2022 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#pragma once 4#pragma once
@@ -6,6 +6,7 @@
6#include <span> 6#include <span>
7 7
8#include "audio_core/common/common.h" 8#include "audio_core/common/common.h"
9#include "audio_core/renderer/command/command_list_header.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
11namespace Core { 12namespace Core {
@@ -20,10 +21,11 @@ namespace Sink {
20class SinkStream; 21class SinkStream;
21} 22}
22 23
23namespace AudioRenderer { 24namespace Renderer {
24struct CommandListHeader; 25struct CommandListHeader;
26}
25 27
26namespace ADSP { 28namespace ADSP::AudioRenderer {
27 29
28/** 30/**
29 * A processor for command lists given to the AudioRenderer. 31 * A processor for command lists given to the AudioRenderer.
@@ -55,14 +57,6 @@ public:
55 u32 GetRemainingCommandCount() const; 57 u32 GetRemainingCommandCount() const;
56 58
57 /** 59 /**
58 * Set the command buffer.
59 *
60 * @param buffer - The buffer to use.
61 * @param size - The size of the buffer.
62 */
63 void SetBuffer(CpuAddr buffer, u64 size);
64
65 /**
66 * Get the stream for this command list. 60 * Get the stream for this command list.
67 * 61 *
68 * @return The stream associated with this command list. 62 * @return The stream associated with this command list.
@@ -85,7 +79,7 @@ public:
85 /// Stream for the processed samples 79 /// Stream for the processed samples
86 Sink::SinkStream* stream{}; 80 Sink::SinkStream* stream{};
87 /// Header info for this command list 81 /// Header info for this command list
88 CommandListHeader* header{}; 82 Renderer::CommandListHeader* header{};
89 /// The command buffer 83 /// The command buffer
90 u8* commands{}; 84 u8* commands{};
91 /// The command buffer size 85 /// The command buffer size
@@ -114,6 +108,5 @@ public:
114 std::string last_dump{}; 108 std::string last_dump{};
115}; 109};
116 110
117} // namespace ADSP 111} // namespace ADSP::AudioRenderer
118} // namespace AudioRenderer
119} // namespace AudioCore 112} // namespace AudioCore
diff --git a/src/audio_core/adsp/mailbox.h b/src/audio_core/adsp/mailbox.h
new file mode 100644
index 000000000..c31b73717
--- /dev/null
+++ b/src/audio_core/adsp/mailbox.h
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bounded_threadsafe_queue.h"
7#include "common/common_types.h"
8
9namespace AudioCore::ADSP {
10
11enum class AppMailboxId : u32 {
12 Invalid = 0,
13 AudioRenderer = 50,
14 AudioRendererMemoryMapUnmap = 51,
15};
16
17enum class Direction : u32 {
18 Host,
19 DSP,
20};
21
22struct MailboxMessage {
23 u32 msg;
24 std::span<u8> data;
25};
26
27class Mailbox {
28public:
29 void Initialize(AppMailboxId id_) {
30 Reset();
31 id = id_;
32 }
33
34 AppMailboxId Id() const noexcept {
35 return id;
36 }
37
38 void Send(Direction dir, MailboxMessage&& message) {
39 auto& queue = dir == Direction::Host ? host_queue : adsp_queue;
40 queue.EmplaceWait(std::move(message));
41 }
42
43 MailboxMessage Receive(Direction dir, bool block = true) {
44 auto& queue = dir == Direction::Host ? host_queue : adsp_queue;
45 MailboxMessage t;
46 if (block) {
47 queue.PopWait(t);
48 } else {
49 queue.TryPop(t);
50 }
51 return t;
52 }
53
54 void Reset() {
55 id = AppMailboxId::Invalid;
56 MailboxMessage t;
57 while (host_queue.TryPop(t)) {
58 }
59 while (adsp_queue.TryPop(t)) {
60 }
61 }
62
63private:
64 AppMailboxId id{0};
65 Common::SPSCQueue<MailboxMessage> host_queue;
66 Common::SPSCQueue<MailboxMessage> adsp_queue;
67};
68
69} // namespace AudioCore::ADSP
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
index 703ef4494..fcaab2b32 100644
--- a/src/audio_core/audio_core.cpp
+++ b/src/audio_core/audio_core.cpp
@@ -11,7 +11,7 @@ namespace AudioCore {
11AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} { 11AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
12 CreateSinks(); 12 CreateSinks();
13 // Must be created after the sinks 13 // Must be created after the sinks
14 adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink); 14 adsp = std::make_unique<ADSP::ADSP>(system, *output_sink);
15} 15}
16 16
17AudioCore ::~AudioCore() { 17AudioCore ::~AudioCore() {
@@ -43,7 +43,7 @@ Sink::Sink& AudioCore::GetInputSink() {
43 return *input_sink; 43 return *input_sink;
44} 44}
45 45
46AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() { 46ADSP::ADSP& AudioCore::ADSP() {
47 return *adsp; 47 return *adsp;
48} 48}
49 49
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h
index ea047773e..e4e27fc66 100644
--- a/src/audio_core/audio_core.h
+++ b/src/audio_core/audio_core.h
@@ -5,8 +5,8 @@
5 5
6#include <memory> 6#include <memory>
7 7
8#include "audio_core/adsp/adsp.h"
8#include "audio_core/audio_manager.h" 9#include "audio_core/audio_manager.h"
9#include "audio_core/renderer/adsp/adsp.h"
10#include "audio_core/sink/sink.h" 10#include "audio_core/sink/sink.h"
11 11
12namespace Core { 12namespace Core {
@@ -55,7 +55,7 @@ public:
55 * 55 *
56 * @return Ref to the ADSP. 56 * @return Ref to the ADSP.
57 */ 57 */
58 AudioRenderer::ADSP::ADSP& GetADSP(); 58 ADSP::ADSP& ADSP();
59 59
60private: 60private:
61 /** 61 /**
@@ -70,7 +70,7 @@ private:
70 /// Sink used for audio input 70 /// Sink used for audio input
71 std::unique_ptr<Sink::Sink> input_sink; 71 std::unique_ptr<Sink::Sink> input_sink;
72 /// The ADSP in the sysmodule 72 /// The ADSP in the sysmodule
73 std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp; 73 std::unique_ptr<ADSP::ADSP> adsp;
74}; 74};
75 75
76} // namespace AudioCore 76} // namespace AudioCore
diff --git a/src/audio_core/audio_event.cpp b/src/audio_core/audio_event.cpp
index d15568e1f..c23ef0990 100644
--- a/src/audio_core/audio_event.cpp
+++ b/src/audio_core/audio_event.cpp
@@ -20,7 +20,6 @@ size_t Event::GetManagerIndex(const Type type) const {
20 default: 20 default:
21 UNREACHABLE(); 21 UNREACHABLE();
22 } 22 }
23 return 3;
24} 23}
25 24
26void Event::SetAudioEvent(const Type type, const bool signalled) { 25void Event::SetAudioEvent(const Type type, const bool signalled) {
diff --git a/src/audio_core/audio_in_manager.cpp b/src/audio_core/audio_in_manager.cpp
index 3dfb613cb..a3667524f 100644
--- a/src/audio_core/audio_in_manager.cpp
+++ b/src/audio_core/audio_in_manager.cpp
@@ -73,7 +73,7 @@ void Manager::BufferReleaseAndRegister() {
73 } 73 }
74} 74}
75 75
76u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, 76u32 Manager::GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names,
77 [[maybe_unused]] const u32 max_count, 77 [[maybe_unused]] const u32 max_count,
78 [[maybe_unused]] const bool filter) { 78 [[maybe_unused]] const bool filter) {
79 std::scoped_lock l{mutex}; 79 std::scoped_lock l{mutex};
diff --git a/src/audio_core/audio_in_manager.h b/src/audio_core/audio_in_manager.h
index 8a519df99..5c4614cd1 100644
--- a/src/audio_core/audio_in_manager.h
+++ b/src/audio_core/audio_in_manager.h
@@ -65,8 +65,8 @@ public:
65 * 65 *
66 * @return Number of names written. 66 * @return Number of names written.
67 */ 67 */
68 u32 GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names, 68 u32 GetDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names, u32 max_count,
69 u32 max_count, bool filter); 69 bool filter);
70 70
71 /// Core system 71 /// Core system
72 Core::System& system; 72 Core::System& system;
diff --git a/src/audio_core/audio_out_manager.cpp b/src/audio_core/audio_out_manager.cpp
index f22821360..316ea7c81 100644
--- a/src/audio_core/audio_out_manager.cpp
+++ b/src/audio_core/audio_out_manager.cpp
@@ -73,7 +73,7 @@ void Manager::BufferReleaseAndRegister() {
73} 73}
74 74
75u32 Manager::GetAudioOutDeviceNames( 75u32 Manager::GetAudioOutDeviceNames(
76 std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const { 76 std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
77 names.emplace_back("DeviceOut"); 77 names.emplace_back("DeviceOut");
78 return 1; 78 return 1;
79} 79}
diff --git a/src/audio_core/audio_out_manager.h b/src/audio_core/audio_out_manager.h
index 1e05ec5ed..c3e445d5d 100644
--- a/src/audio_core/audio_out_manager.h
+++ b/src/audio_core/audio_out_manager.h
@@ -61,8 +61,7 @@ public:
61 * @param names - Output container to write names to. 61 * @param names - Output container to write names to.
62 * @return Number of names written. 62 * @return Number of names written.
63 */ 63 */
64 u32 GetAudioOutDeviceNames( 64 u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
65 std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const;
66 65
67 /// Core system 66 /// Core system
68 Core::System& system; 67 Core::System& system;
diff --git a/src/audio_core/audio_render_manager.cpp b/src/audio_core/audio_render_manager.cpp
index 320715727..3c53e3afd 100644
--- a/src/audio_core/audio_render_manager.cpp
+++ b/src/audio_core/audio_render_manager.cpp
@@ -6,7 +6,7 @@
6#include "audio_core/common/feature_support.h" 6#include "audio_core/common/feature_support.h"
7#include "core/core.h" 7#include "core/core.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11Manager::Manager(Core::System& system_) 11Manager::Manager(Core::System& system_)
12 : system{system_}, system_manager{std::make_unique<SystemManager>(system)} { 12 : system{system_}, system_manager{std::make_unique<SystemManager>(system)} {
@@ -67,4 +67,4 @@ bool Manager::RemoveSystem(System& system_) {
67 return system_manager->Remove(system_); 67 return system_manager->Remove(system_);
68} 68}
69 69
70} // namespace AudioCore::AudioRenderer 70} // namespace AudioCore::Renderer
diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h
index fffa5944d..45537b270 100644
--- a/src/audio_core/audio_render_manager.h
+++ b/src/audio_core/audio_render_manager.h
@@ -20,7 +20,7 @@ class System;
20namespace AudioCore { 20namespace AudioCore {
21struct AudioRendererParameterInternal; 21struct AudioRendererParameterInternal;
22 22
23namespace AudioRenderer { 23namespace Renderer {
24/** 24/**
25 * Wrapper for the audio system manager, handles service calls. 25 * Wrapper for the audio system manager, handles service calls.
26 */ 26 */
@@ -101,5 +101,5 @@ private:
101 std::unique_ptr<SystemManager> system_manager{}; 101 std::unique_ptr<SystemManager> system_manager{};
102}; 102};
103 103
104} // namespace AudioRenderer 104} // namespace Renderer
105} // namespace AudioCore 105} // namespace AudioCore
diff --git a/src/audio_core/common/audio_renderer_parameter.h b/src/audio_core/common/audio_renderer_parameter.h
index 8c7892bcf..6c4e9fdc6 100644
--- a/src/audio_core/common/audio_renderer_parameter.h
+++ b/src/audio_core/common/audio_renderer_parameter.h
@@ -51,10 +51,10 @@ struct AudioRendererSystemContext {
51 s32 session_id; 51 s32 session_id;
52 s8 channels; 52 s8 channels;
53 s16 mix_buffer_count; 53 s16 mix_buffer_count;
54 AudioRenderer::BehaviorInfo* behavior; 54 Renderer::BehaviorInfo* behavior;
55 std::span<s32> depop_buffer; 55 std::span<s32> depop_buffer;
56 AudioRenderer::UpsamplerManager* upsampler_manager; 56 Renderer::UpsamplerManager* upsampler_manager;
57 AudioRenderer::MemoryPoolInfo* memory_pool_info; 57 Renderer::MemoryPoolInfo* memory_pool_info;
58}; 58};
59 59
60} // namespace AudioCore 60} // namespace AudioCore
diff --git a/src/audio_core/renderer/adsp/adsp.cpp b/src/audio_core/renderer/adsp/adsp.cpp
deleted file mode 100644
index b1db31e93..000000000
--- a/src/audio_core/renderer/adsp/adsp.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/adsp.h"
5#include "audio_core/renderer/adsp/command_buffer.h"
6#include "audio_core/sink/sink.h"
7#include "common/logging/log.h"
8#include "core/core.h"
9#include "core/core_timing.h"
10#include "core/memory.h"
11
12namespace AudioCore::AudioRenderer::ADSP {
13
14ADSP::ADSP(Core::System& system_, Sink::Sink& sink_)
15 : system{system_}, memory{system.ApplicationMemory()}, sink{sink_} {}
16
17ADSP::~ADSP() {
18 ClearCommandBuffers();
19}
20
21State ADSP::GetState() const {
22 if (running) {
23 return State::Started;
24 }
25 return State::Stopped;
26}
27
28AudioRenderer_Mailbox* ADSP::GetRenderMailbox() {
29 return &render_mailbox;
30}
31
32void ADSP::ClearRemainCount(const u32 session_id) {
33 render_mailbox.ClearRemainCount(session_id);
34}
35
36u64 ADSP::GetSignalledTick() const {
37 return render_mailbox.GetSignalledTick();
38}
39
40u64 ADSP::GetTimeTaken() const {
41 return render_mailbox.GetRenderTimeTaken();
42}
43
44u64 ADSP::GetRenderTimeTaken(const u32 session_id) {
45 return render_mailbox.GetCommandBuffer(session_id).render_time_taken;
46}
47
48u32 ADSP::GetRemainCommandCount(const u32 session_id) const {
49 return render_mailbox.GetRemainCommandCount(session_id);
50}
51
52void ADSP::SendCommandBuffer(const u32 session_id, const CommandBuffer& command_buffer) {
53 render_mailbox.SetCommandBuffer(session_id, command_buffer);
54}
55
56u64 ADSP::GetRenderingStartTick(const u32 session_id) {
57 return render_mailbox.GetSignalledTick() +
58 render_mailbox.GetCommandBuffer(session_id).render_time_taken;
59}
60
61bool ADSP::Start() {
62 if (running) {
63 return running;
64 }
65
66 running = true;
67 systems_active++;
68 audio_renderer = std::make_unique<AudioRenderer>(system);
69 audio_renderer->Start(&render_mailbox);
70 render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_InitializeOK);
71 if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) {
72 LOG_ERROR(
73 Service_Audio,
74 "Host Audio Renderer -- Failed to receive initialize message response from ADSP!");
75 }
76 return running;
77}
78
79void ADSP::Stop() {
80 systems_active--;
81 if (running && systems_active == 0) {
82 {
83 std::scoped_lock l{mailbox_lock};
84 render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Shutdown);
85 if (render_mailbox.HostWaitMessage() != RenderMessage::AudioRenderer_Shutdown) {
86 LOG_ERROR(Service_Audio, "Host Audio Renderer -- Failed to receive shutdown "
87 "message response from ADSP!");
88 }
89 }
90 audio_renderer->Stop();
91 running = false;
92 }
93}
94
95void ADSP::Signal() {
96 const auto signalled_tick{system.CoreTiming().GetClockTicks()};
97 render_mailbox.SetSignalledTick(signalled_tick);
98 render_mailbox.HostSendMessage(RenderMessage::AudioRenderer_Render);
99}
100
101void ADSP::Wait() {
102 std::scoped_lock l{mailbox_lock};
103 auto response{render_mailbox.HostWaitMessage()};
104 if (response != RenderMessage::AudioRenderer_RenderResponse) {
105 LOG_ERROR(Service_Audio, "Invalid ADSP response message, expected 0x{:02X}, got 0x{:02X}",
106 static_cast<u32>(RenderMessage::AudioRenderer_RenderResponse),
107 static_cast<u32>(response));
108 }
109
110 ClearCommandBuffers();
111}
112
113void ADSP::ClearCommandBuffers() {
114 render_mailbox.ClearCommandBuffers();
115}
116
117} // namespace AudioCore::AudioRenderer::ADSP
diff --git a/src/audio_core/renderer/adsp/adsp.h b/src/audio_core/renderer/adsp/adsp.h
deleted file mode 100644
index f7a2f25e4..000000000
--- a/src/audio_core/renderer/adsp/adsp.h
+++ /dev/null
@@ -1,171 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8
9#include "audio_core/renderer/adsp/audio_renderer.h"
10#include "common/common_types.h"
11
12namespace Core {
13namespace Memory {
14class Memory;
15}
16class System;
17} // namespace Core
18
19namespace AudioCore {
20namespace Sink {
21class Sink;
22}
23
24namespace AudioRenderer::ADSP {
25struct CommandBuffer;
26
27enum class State {
28 Started,
29 Stopped,
30};
31
32/**
33 * Represents the ADSP embedded within the audio sysmodule.
34 * This is a 32-bit Linux4Tegra kernel from nVidia, which is launched with the sysmodule on boot.
35 *
36 * The kernel will run apps you program for it, Nintendo have the following:
37 *
38 * Gmix - Responsible for mixing final audio and sending it out to hardware. This is last place all
39 * audio samples end up, and we skip it entirely, since we have very different backends and
40 * mixing is implicitly handled by the OS (but also due to lack of research/simplicity).
41 *
42 * AudioRenderer - Receives command lists generated by the audio render
43 * system, processes them, and sends the samples to Gmix.
44 *
45 * OpusDecoder - Contains libopus, and controls processing Opus audio and sends it to Gmix.
46 * Not much research done here, TODO if needed.
47 *
48 * We only implement the AudioRenderer for now.
49 *
50 * Communication for the apps is done through mailboxes, and some shared memory.
51 */
52class ADSP {
53public:
54 explicit ADSP(Core::System& system, Sink::Sink& sink);
55 ~ADSP();
56
57 /**
58 * Start the ADSP.
59 *
60 * @return True if started or already running, otherwise false.
61 */
62 bool Start();
63
64 /**
65 * Stop the ADSP.
66 */
67 void Stop();
68
69 /**
70 * Get the ADSP's state.
71 *
72 * @return Started or Stopped.
73 */
74 State GetState() const;
75
76 /**
77 * Get the AudioRenderer mailbox to communicate with it.
78 *
79 * @return The AudioRenderer mailbox.
80 */
81 AudioRenderer_Mailbox* GetRenderMailbox();
82
83 /**
84 * Get the tick the ADSP was signalled.
85 *
86 * @return The tick the ADSP was signalled.
87 */
88 u64 GetSignalledTick() const;
89
90 /**
91 * Get the total time it took for the ADSP to run the last command lists (both command lists).
92 *
93 * @return The tick the ADSP was signalled.
94 */
95 u64 GetTimeTaken() const;
96
97 /**
98 * Get the last time a given command list took to run.
99 *
100 * @param session_id - The session id to check (0 or 1).
101 * @return The time it took.
102 */
103 u64 GetRenderTimeTaken(u32 session_id);
104
105 /**
106 * Clear the remaining command count for a given session.
107 *
108 * @param session_id - The session id to check (0 or 1).
109 */
110 void ClearRemainCount(u32 session_id);
111
112 /**
113 * Get the remaining number of commands left to process for a command list.
114 *
115 * @param session_id - The session id to check (0 or 1).
116 * @return The number of commands remaining.
117 */
118 u32 GetRemainCommandCount(u32 session_id) const;
119
120 /**
121 * Get the last tick a command list started processing.
122 *
123 * @param session_id - The session id to check (0 or 1).
124 * @return The last tick the given command list started.
125 */
126 u64 GetRenderingStartTick(u32 session_id);
127
128 /**
129 * Set a command buffer to be processed.
130 *
131 * @param session_id - The session id to check (0 or 1).
132 * @param command_buffer - The command buffer to process.
133 */
134 void SendCommandBuffer(u32 session_id, const CommandBuffer& command_buffer);
135
136 /**
137 * Clear the command buffers (does not clear the time taken or the remaining command count)
138 */
139 void ClearCommandBuffers();
140
141 /**
142 * Signal the AudioRenderer to begin processing.
143 */
144 void Signal();
145
146 /**
147 * Wait for the AudioRenderer to finish processing.
148 */
149 void Wait();
150
151private:
152 /// Core system
153 Core::System& system;
154 /// Core memory
155 Core::Memory::Memory& memory;
156 /// Number of systems active, used to prevent accidental shutdowns
157 u8 systems_active{0};
158 /// ADSP running state
159 std::atomic<bool> running{false};
160 /// Output sink used by the ADSP
161 Sink::Sink& sink;
162 /// AudioRenderer app
163 std::unique_ptr<AudioRenderer> audio_renderer{};
164 /// Communication for the AudioRenderer
165 AudioRenderer_Mailbox render_mailbox{};
166 /// Mailbox lock ffor the render mailbox
167 std::mutex mailbox_lock;
168};
169
170} // namespace AudioRenderer::ADSP
171} // namespace AudioCore
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp
deleted file mode 100644
index 9ca716b60..000000000
--- a/src/audio_core/renderer/adsp/audio_renderer.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5#include <chrono>
6
7#include "audio_core/audio_core.h"
8#include "audio_core/common/common.h"
9#include "audio_core/renderer/adsp/audio_renderer.h"
10#include "audio_core/sink/sink.h"
11#include "common/logging/log.h"
12#include "common/microprofile.h"
13#include "common/thread.h"
14#include "core/core.h"
15#include "core/core_timing.h"
16
17MICROPROFILE_DEFINE(Audio_Renderer, "Audio", "DSP", MP_RGB(60, 19, 97));
18
19namespace AudioCore::AudioRenderer::ADSP {
20
21void AudioRenderer_Mailbox::HostSendMessage(RenderMessage message_) {
22 adsp_messages.enqueue(message_);
23 adsp_event.Set();
24}
25
26RenderMessage AudioRenderer_Mailbox::HostWaitMessage() {
27 host_event.Wait();
28 RenderMessage msg{RenderMessage::Invalid};
29 if (!host_messages.try_dequeue(msg)) {
30 LOG_ERROR(Service_Audio, "Failed to dequeue host message!");
31 }
32 return msg;
33}
34
35void AudioRenderer_Mailbox::ADSPSendMessage(const RenderMessage message_) {
36 host_messages.enqueue(message_);
37 host_event.Set();
38}
39
40RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() {
41 adsp_event.Wait();
42 RenderMessage msg{RenderMessage::Invalid};
43 if (!adsp_messages.try_dequeue(msg)) {
44 LOG_ERROR(Service_Audio, "Failed to dequeue ADSP message!");
45 }
46 return msg;
47}
48
49CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) {
50 return command_buffers[session_id];
51}
52
53void AudioRenderer_Mailbox::SetCommandBuffer(const u32 session_id, const CommandBuffer& buffer) {
54 command_buffers[session_id] = buffer;
55}
56
57u64 AudioRenderer_Mailbox::GetRenderTimeTaken() const {
58 return command_buffers[0].render_time_taken + command_buffers[1].render_time_taken;
59}
60
61u64 AudioRenderer_Mailbox::GetSignalledTick() const {
62 return signalled_tick;
63}
64
65void AudioRenderer_Mailbox::SetSignalledTick(const u64 tick) {
66 signalled_tick = tick;
67}
68
69void AudioRenderer_Mailbox::ClearRemainCount(const u32 session_id) {
70 command_buffers[session_id].remaining_command_count = 0;
71}
72
73u32 AudioRenderer_Mailbox::GetRemainCommandCount(const u32 session_id) const {
74 return command_buffers[session_id].remaining_command_count;
75}
76
77void AudioRenderer_Mailbox::ClearCommandBuffers() {
78 command_buffers[0].buffer = 0;
79 command_buffers[0].size = 0;
80 command_buffers[0].reset_buffers = false;
81 command_buffers[1].buffer = 0;
82 command_buffers[1].size = 0;
83 command_buffers[1].reset_buffers = false;
84}
85
86AudioRenderer::AudioRenderer(Core::System& system_)
87 : system{system_}, sink{system.AudioCore().GetOutputSink()} {
88 CreateSinkStreams();
89}
90
91AudioRenderer::~AudioRenderer() {
92 Stop();
93 for (auto& stream : streams) {
94 if (stream) {
95 sink.CloseStream(stream);
96 }
97 stream = nullptr;
98 }
99}
100
101void AudioRenderer::Start(AudioRenderer_Mailbox* mailbox_) {
102 if (running) {
103 return;
104 }
105
106 mailbox = mailbox_;
107 thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); });
108 running = true;
109}
110
111void AudioRenderer::Stop() {
112 if (!running) {
113 return;
114 }
115
116 for (auto& stream : streams) {
117 stream->Stop();
118 }
119 thread.join();
120 running = false;
121}
122
123void AudioRenderer::CreateSinkStreams() {
124 u32 channels{sink.GetDeviceChannels()};
125 for (u32 i = 0; i < MaxRendererSessions; i++) {
126 std::string name{fmt::format("ADSP_RenderStream-{}", i)};
127 streams[i] =
128 sink.AcquireSinkStream(system, channels, name, ::AudioCore::Sink::StreamType::Render);
129 streams[i]->SetRingSize(4);
130 }
131}
132
133void AudioRenderer::ThreadFunc(std::stop_token stop_token) {
134 static constexpr char name[]{"AudioRenderer"};
135 MicroProfileOnThreadCreate(name);
136 Common::SetCurrentThreadName(name);
137 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
138 if (mailbox->ADSPWaitMessage() != RenderMessage::AudioRenderer_InitializeOK) {
139 LOG_ERROR(Service_Audio,
140 "ADSP Audio Renderer -- Failed to receive initialize message from host!");
141 return;
142 }
143
144 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_InitializeOK);
145
146 // 0.12 seconds (2304000 / 19200000)
147 constexpr u64 max_process_time{2'304'000ULL};
148
149 while (!stop_token.stop_requested()) {
150 auto message{mailbox->ADSPWaitMessage()};
151 switch (message) {
152 case RenderMessage::AudioRenderer_Shutdown:
153 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_Shutdown);
154 return;
155
156 case RenderMessage::AudioRenderer_Render: {
157 if (system.IsShuttingDown()) [[unlikely]] {
158 std::this_thread::sleep_for(std::chrono::milliseconds(5));
159 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse);
160 continue;
161 }
162 std::array<bool, MaxRendererSessions> buffers_reset{};
163 std::array<u64, MaxRendererSessions> render_times_taken{};
164 const auto start_time{system.CoreTiming().GetClockTicks()};
165
166 for (u32 index = 0; index < 2; index++) {
167 auto& command_buffer{mailbox->GetCommandBuffer(index)};
168 auto& command_list_processor{command_list_processors[index]};
169
170 // Check this buffer is valid, as it may not be used.
171 if (command_buffer.buffer != 0) {
172 // If there are no remaining commands (from the previous list),
173 // this is a new command list, initialize it.
174 if (command_buffer.remaining_command_count == 0) {
175 command_list_processor.Initialize(system, command_buffer.buffer,
176 command_buffer.size, streams[index]);
177 }
178
179 if (command_buffer.reset_buffers && !buffers_reset[index]) {
180 streams[index]->ClearQueue();
181 buffers_reset[index] = true;
182 }
183
184 u64 max_time{max_process_time};
185 if (index == 1 && command_buffer.applet_resource_user_id ==
186 mailbox->GetCommandBuffer(0).applet_resource_user_id) {
187 max_time = max_process_time - render_times_taken[0];
188 if (render_times_taken[0] > max_process_time) {
189 max_time = 0;
190 }
191 }
192
193 max_time = std::min(command_buffer.time_limit, max_time);
194 command_list_processor.SetProcessTimeMax(max_time);
195
196 streams[index]->WaitFreeSpace(stop_token);
197
198 // Process the command list
199 {
200 MICROPROFILE_SCOPE(Audio_Renderer);
201 render_times_taken[index] =
202 command_list_processor.Process(index) - start_time;
203 }
204
205 const auto end_time{system.CoreTiming().GetClockTicks()};
206
207 command_buffer.remaining_command_count =
208 command_list_processor.GetRemainingCommandCount();
209 command_buffer.render_time_taken = end_time - start_time;
210 }
211 }
212
213 mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse);
214 } break;
215
216 default:
217 LOG_WARNING(Service_Audio,
218 "ADSP AudioRenderer received an invalid message, msg={:02X}!",
219 static_cast<u32>(message));
220 break;
221 }
222 }
223}
224
225} // namespace AudioCore::AudioRenderer::ADSP
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h
deleted file mode 100644
index 88e558183..000000000
--- a/src/audio_core/renderer/adsp/audio_renderer.h
+++ /dev/null
@@ -1,204 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <thread>
9
10#include "audio_core/renderer/adsp/command_buffer.h"
11#include "audio_core/renderer/adsp/command_list_processor.h"
12#include "common/common_types.h"
13#include "common/polyfill_thread.h"
14#include "common/reader_writer_queue.h"
15#include "common/thread.h"
16
17namespace Core {
18namespace Timing {
19struct EventType;
20}
21class System;
22} // namespace Core
23
24namespace AudioCore {
25namespace Sink {
26class Sink;
27}
28
29namespace AudioRenderer::ADSP {
30
31enum class RenderMessage {
32 /* 0x00 */ Invalid,
33 /* 0x01 */ AudioRenderer_MapUnmap_Map,
34 /* 0x02 */ AudioRenderer_MapUnmap_MapResponse,
35 /* 0x03 */ AudioRenderer_MapUnmap_Unmap,
36 /* 0x04 */ AudioRenderer_MapUnmap_UnmapResponse,
37 /* 0x05 */ AudioRenderer_MapUnmap_InvalidateCache,
38 /* 0x06 */ AudioRenderer_MapUnmap_InvalidateCacheResponse,
39 /* 0x07 */ AudioRenderer_MapUnmap_Shutdown,
40 /* 0x08 */ AudioRenderer_MapUnmap_ShutdownResponse,
41 /* 0x16 */ AudioRenderer_InitializeOK = 0x16,
42 /* 0x20 */ AudioRenderer_RenderResponse = 0x20,
43 /* 0x2A */ AudioRenderer_Render = 0x2A,
44 /* 0x34 */ AudioRenderer_Shutdown = 0x34,
45};
46
47/**
48 * A mailbox for the AudioRenderer, allowing communication between the host and the AudioRenderer
49 * running on the ADSP.
50 */
51class AudioRenderer_Mailbox {
52public:
53 /**
54 * Send a message from the host to the AudioRenderer.
55 *
56 * @param message - The message to send to the AudioRenderer.
57 */
58 void HostSendMessage(RenderMessage message);
59
60 /**
61 * Host wait for a message from the AudioRenderer.
62 *
63 * @return The message returned from the AudioRenderer.
64 */
65 RenderMessage HostWaitMessage();
66
67 /**
68 * Send a message from the AudioRenderer to the host.
69 *
70 * @param message - The message to send to the host.
71 */
72 void ADSPSendMessage(RenderMessage message);
73
74 /**
75 * AudioRenderer wait for a message from the host.
76 *
77 * @return The message returned from the AudioRenderer.
78 */
79 RenderMessage ADSPWaitMessage();
80
81 /**
82 * Get the command buffer with the given session id (0 or 1).
83 *
84 * @param session_id - The session id to get (0 or 1).
85 * @return The command buffer.
86 */
87 CommandBuffer& GetCommandBuffer(u32 session_id);
88
89 /**
90 * Set the command buffer with the given session id (0 or 1).
91 *
92 * @param session_id - The session id to get (0 or 1).
93 * @param buffer - The command buffer to set.
94 */
95 void SetCommandBuffer(u32 session_id, const CommandBuffer& buffer);
96
97 /**
98 * Get the total render time taken for the last command lists sent.
99 *
100 * @return Total render time taken for the last command lists.
101 */
102 u64 GetRenderTimeTaken() const;
103
104 /**
105 * Get the tick the AudioRenderer was signalled.
106 *
107 * @return The tick the AudioRenderer was signalled.
108 */
109 u64 GetSignalledTick() const;
110
111 /**
112 * Set the tick the AudioRenderer was signalled.
113 *
114 * @param tick - The tick the AudioRenderer was signalled.
115 */
116 void SetSignalledTick(u64 tick);
117
118 /**
119 * Clear the remaining command count.
120 *
121 * @param session_id - Index for which command list to clear (0 or 1).
122 */
123 void ClearRemainCount(u32 session_id);
124
125 /**
126 * Get the remaining command count for a given command list.
127 *
128 * @param session_id - Index for which command list to clear (0 or 1).
129 * @return The remaining command count.
130 */
131 u32 GetRemainCommandCount(u32 session_id) const;
132
133 /**
134 * Clear the command buffers (does not clear the time taken or the remaining command count).
135 */
136 void ClearCommandBuffers();
137
138private:
139 /// Host signalling event
140 Common::Event host_event{};
141 /// AudioRenderer signalling event
142 Common::Event adsp_event{};
143 /// Host message queue
144
145 Common::ReaderWriterQueue<RenderMessage> host_messages{};
146 /// AudioRenderer message queue
147
148 Common::ReaderWriterQueue<RenderMessage> adsp_messages{};
149 /// Command buffers
150
151 std::array<CommandBuffer, MaxRendererSessions> command_buffers{};
152 /// Tick the AudioRnederer was signalled
153 u64 signalled_tick{};
154};
155
156/**
157 * The AudioRenderer application running on the ADSP.
158 */
159class AudioRenderer {
160public:
161 explicit AudioRenderer(Core::System& system);
162 ~AudioRenderer();
163
164 /**
165 * Start the AudioRenderer.
166 *
167 * @param mailbox The mailbox to use for this session.
168 */
169 void Start(AudioRenderer_Mailbox* mailbox);
170
171 /**
172 * Stop the AudioRenderer.
173 */
174 void Stop();
175
176private:
177 /**
178 * Main AudioRenderer thread, responsible for processing the command lists.
179 */
180 void ThreadFunc(std::stop_token stop_token);
181
182 /**
183 * Creates the streams which will receive the processed samples.
184 */
185 void CreateSinkStreams();
186
187 /// Core system
188 Core::System& system;
189 /// Main thread
190 std::jthread thread{};
191 /// The current state
192 std::atomic<bool> running{};
193 /// The active mailbox
194 AudioRenderer_Mailbox* mailbox{};
195 /// The command lists to process
196 std::array<CommandListProcessor, MaxRendererSessions> command_list_processors{};
197 /// The output sink the AudioRenderer will use
198 Sink::Sink& sink;
199 /// The streams which will receive the processed samples
200 std::array<Sink::SinkStream*, MaxRendererSessions> streams;
201};
202
203} // namespace AudioRenderer::ADSP
204} // namespace AudioCore
diff --git a/src/audio_core/renderer/adsp/command_buffer.h b/src/audio_core/renderer/adsp/command_buffer.h
deleted file mode 100644
index 880b279d8..000000000
--- a/src/audio_core/renderer/adsp/command_buffer.h
+++ /dev/null
@@ -1,21 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/common/common.h"
7#include "common/common_types.h"
8
9namespace AudioCore::AudioRenderer::ADSP {
10
11struct CommandBuffer {
12 CpuAddr buffer;
13 u64 size;
14 u64 time_limit;
15 u32 remaining_command_count;
16 bool reset_buffers;
17 u64 applet_resource_user_id;
18 u64 render_time_taken;
19};
20
21} // namespace AudioCore::AudioRenderer::ADSP
diff --git a/src/audio_core/renderer/audio_device.cpp b/src/audio_core/renderer/audio_device.cpp
index 0d9d8f6ce..2d9bf82bb 100644
--- a/src/audio_core/renderer/audio_device.cpp
+++ b/src/audio_core/renderer/audio_device.cpp
@@ -10,7 +10,7 @@
10#include "audio_core/sink/sink.h" 10#include "audio_core/sink/sink.h"
11#include "core/core.h" 11#include "core/core.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14 14
15constexpr std::array usb_device_names{ 15constexpr std::array usb_device_names{
16 AudioDevice::AudioDeviceName{"AudioStereoJackOutput"}, 16 AudioDevice::AudioDeviceName{"AudioStereoJackOutput"},
@@ -71,4 +71,4 @@ f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const {
71 return output_sink.GetDeviceVolume(); 71 return output_sink.GetDeviceVolume();
72} 72}
73 73
74} // namespace AudioCore::AudioRenderer 74} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/audio_device.h b/src/audio_core/renderer/audio_device.h
index dd6be70ee..ca4040add 100644
--- a/src/audio_core/renderer/audio_device.h
+++ b/src/audio_core/renderer/audio_device.h
@@ -16,7 +16,7 @@ namespace Sink {
16class Sink; 16class Sink;
17} 17}
18 18
19namespace AudioRenderer { 19namespace Renderer {
20/** 20/**
21 * An interface to an output audio device available to the Switch. 21 * An interface to an output audio device available to the Switch.
22 */ 22 */
@@ -76,5 +76,5 @@ private:
76 const u32 user_revision; 76 const u32 user_revision;
77}; 77};
78 78
79} // namespace AudioRenderer 79} // namespace Renderer
80} // namespace AudioCore 80} // namespace AudioCore
diff --git a/src/audio_core/renderer/audio_renderer.cpp b/src/audio_core/renderer/audio_renderer.cpp
index a8257eb2e..09efe9be9 100644
--- a/src/audio_core/renderer/audio_renderer.cpp
+++ b/src/audio_core/renderer/audio_renderer.cpp
@@ -9,7 +9,7 @@
9#include "core/hle/kernel/k_transfer_memory.h" 9#include "core/hle/kernel/k_transfer_memory.h"
10#include "core/hle/service/audio/errors.h" 10#include "core/hle/service/audio/errors.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13 13
14Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) 14Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
15 : core{system_}, manager{manager_}, system{system_, rendered_event} {} 15 : core{system_}, manager{manager_}, system{system_, rendered_event} {}
@@ -64,4 +64,4 @@ Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performa
64 return system.Update(input, performance, output); 64 return system.Update(input, performance, output);
65} 65}
66 66
67} // namespace AudioCore::AudioRenderer 67} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/audio_renderer.h b/src/audio_core/renderer/audio_renderer.h
index 90c6f9727..24650278b 100644
--- a/src/audio_core/renderer/audio_renderer.h
+++ b/src/audio_core/renderer/audio_renderer.h
@@ -19,7 +19,7 @@ class KTransferMemory;
19namespace AudioCore { 19namespace AudioCore {
20struct AudioRendererParameterInternal; 20struct AudioRendererParameterInternal;
21 21
22namespace AudioRenderer { 22namespace Renderer {
23class Manager; 23class Manager;
24 24
25/** 25/**
@@ -31,7 +31,7 @@ public:
31 31
32 /** 32 /**
33 * Initialize the renderer. 33 * Initialize the renderer.
34 * Registers the system with the AudioRenderer::Manager, allocates workbuffers and initializes 34 * Registers the system with the Renderer::Manager, allocates workbuffers and initializes
35 * everything to a default state. 35 * everything to a default state.
36 * 36 *
37 * @param params - Input parameters to initialize the system with. 37 * @param params - Input parameters to initialize the system with.
@@ -93,5 +93,5 @@ private:
93 System system; 93 System system;
94}; 94};
95 95
96} // namespace AudioRenderer 96} // namespace Renderer
97} // namespace AudioCore 97} // namespace AudioCore
diff --git a/src/audio_core/renderer/behavior/behavior_info.cpp b/src/audio_core/renderer/behavior/behavior_info.cpp
index 3d2a91312..058539042 100644
--- a/src/audio_core/renderer/behavior/behavior_info.cpp
+++ b/src/audio_core/renderer/behavior/behavior_info.cpp
@@ -4,7 +4,7 @@
4#include "audio_core/common/feature_support.h" 4#include "audio_core/common/feature_support.h"
5#include "audio_core/renderer/behavior/behavior_info.h" 5#include "audio_core/renderer/behavior/behavior_info.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8 8
9BehaviorInfo::BehaviorInfo() : process_revision{CurrentRevision} {} 9BehaviorInfo::BehaviorInfo() : process_revision{CurrentRevision} {}
10 10
@@ -190,4 +190,4 @@ bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const {
190 return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision); 190 return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision);
191} 191}
192 192
193} // namespace AudioCore::AudioRenderer 193} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/behavior/behavior_info.h b/src/audio_core/renderer/behavior/behavior_info.h
index b52340229..a4958857a 100644
--- a/src/audio_core/renderer/behavior/behavior_info.h
+++ b/src/audio_core/renderer/behavior/behavior_info.h
@@ -10,7 +10,7 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/audio/errors.h" 11#include "core/hle/service/audio/errors.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14/** 14/**
15 * Holds host and user revisions, checks whether render features can be enabled, and reports errors. 15 * Holds host and user revisions, checks whether render features can be enabled, and reports errors.
16 */ 16 */
@@ -264,7 +264,7 @@ public:
264 /** 264 /**
265 * Check if skipping voice pitch and sample rate conversion is supported. 265 * Check if skipping voice pitch and sample rate conversion is supported.
266 * This speeds up the data source commands by skipping resampling if unwanted. 266 * This speeds up the data source commands by skipping resampling if unwanted.
267 * See AudioCore::AudioRenderer::DecodeFromWaveBuffers 267 * See AudioCore::Renderer::DecodeFromWaveBuffers
268 * 268 *
269 * @return True if supported, otherwise false. 269 * @return True if supported, otherwise false.
270 */ 270 */
@@ -273,7 +273,7 @@ public:
273 /** 273 /**
274 * Check if resetting played sample count at loop points is supported. 274 * Check if resetting played sample count at loop points is supported.
275 * This resets the number of samples played in a voice state when a loop point is reached. 275 * This resets the number of samples played in a voice state when a loop point is reached.
276 * See AudioCore::AudioRenderer::DecodeFromWaveBuffers 276 * See AudioCore::Renderer::DecodeFromWaveBuffers
277 * 277 *
278 * @return True if supported, otherwise false. 278 * @return True if supported, otherwise false.
279 */ 279 */
@@ -373,4 +373,4 @@ public:
373 u32 error_count{}; 373 u32 error_count{};
374}; 374};
375 375
376} // namespace AudioCore::AudioRenderer 376} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp
index e312eb166..667711e17 100644
--- a/src/audio_core/renderer/behavior/info_updater.cpp
+++ b/src/audio_core/renderer/behavior/info_updater.cpp
@@ -15,7 +15,7 @@
15#include "audio_core/renderer/splitter/splitter_context.h" 15#include "audio_core/renderer/splitter/splitter_context.h"
16#include "audio_core/renderer/voice/voice_context.h" 16#include "audio_core/renderer/voice/voice_context.h"
17 17
18namespace AudioCore::AudioRenderer { 18namespace AudioCore::Renderer {
19 19
20InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_, 20InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_,
21 const u32 process_handle_, BehaviorInfo& behaviour_) 21 const u32 process_handle_, BehaviorInfo& behaviour_)
@@ -536,4 +536,4 @@ Result InfoUpdater::CheckConsumedSize() {
536 return ResultSuccess; 536 return ResultSuccess;
537} 537}
538 538
539} // namespace AudioCore::AudioRenderer 539} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/behavior/info_updater.h b/src/audio_core/renderer/behavior/info_updater.h
index c817d8d8d..fb4b7d25a 100644
--- a/src/audio_core/renderer/behavior/info_updater.h
+++ b/src/audio_core/renderer/behavior/info_updater.h
@@ -8,7 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/service/audio/errors.h" 9#include "core/hle/service/audio/errors.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::Renderer {
12class BehaviorInfo; 12class BehaviorInfo;
13class VoiceContext; 13class VoiceContext;
14class MixContext; 14class MixContext;
@@ -202,4 +202,4 @@ private:
202 BehaviorInfo& behaviour; 202 BehaviorInfo& behaviour;
203}; 203};
204 204
205} // namespace AudioCore::AudioRenderer 205} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp
index 0bd418306..67d43e69a 100644
--- a/src/audio_core/renderer/command/command_buffer.cpp
+++ b/src/audio_core/renderer/command/command_buffer.cpp
@@ -16,7 +16,7 @@
16#include "audio_core/renderer/voice/voice_info.h" 16#include "audio_core/renderer/voice/voice_info.h"
17#include "audio_core/renderer/voice/voice_state.h" 17#include "audio_core/renderer/voice/voice_state.h"
18 18
19namespace AudioCore::AudioRenderer { 19namespace AudioCore::Renderer {
20 20
21template <typename T, CommandId Id> 21template <typename T, CommandId Id>
22T& CommandBuffer::GenerateStart(const s32 node_id) { 22T& CommandBuffer::GenerateStart(const s32 node_id) {
@@ -713,4 +713,4 @@ void CommandBuffer::GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase&
713 GenerateEnd<CompressorCommand>(cmd); 713 GenerateEnd<CompressorCommand>(cmd);
714} 714}
715 715
716} // namespace AudioCore::AudioRenderer 716} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/command_buffer.h b/src/audio_core/renderer/command/command_buffer.h
index 162170846..12e8c2c81 100644
--- a/src/audio_core/renderer/command/command_buffer.h
+++ b/src/audio_core/renderer/command/command_buffer.h
@@ -10,7 +10,7 @@
10#include "audio_core/renderer/performance/performance_manager.h" 10#include "audio_core/renderer/performance/performance_manager.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14struct UpsamplerInfo; 14struct UpsamplerInfo;
15struct VoiceState; 15struct VoiceState;
16class EffectInfoBase; 16class EffectInfoBase;
@@ -465,4 +465,4 @@ private:
465 void GenerateEnd(T& cmd); 465 void GenerateEnd(T& cmd);
466}; 466};
467 467
468} // namespace AudioCore::AudioRenderer 468} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp
index fba84c7bd..ccb186209 100644
--- a/src/audio_core/renderer/command/command_generator.cpp
+++ b/src/audio_core/renderer/command/command_generator.cpp
@@ -21,7 +21,7 @@
21#include "audio_core/renderer/voice/voice_context.h" 21#include "audio_core/renderer/voice/voice_context.h"
22#include "common/alignment.h" 22#include "common/alignment.h"
23 23
24namespace AudioCore::AudioRenderer { 24namespace AudioCore::Renderer {
25 25
26CommandGenerator::CommandGenerator(CommandBuffer& command_buffer_, 26CommandGenerator::CommandGenerator(CommandBuffer& command_buffer_,
27 const CommandListHeader& command_list_header_, 27 const CommandListHeader& command_list_header_,
@@ -793,4 +793,4 @@ void CommandGenerator::GeneratePerformanceCommand(
793 command_buffer.GeneratePerformanceCommand(node_id, state, entry_addresses); 793 command_buffer.GeneratePerformanceCommand(node_id, state, entry_addresses);
794} 794}
795 795
796} // namespace AudioCore::AudioRenderer 796} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/command_generator.h b/src/audio_core/renderer/command/command_generator.h
index b3cd7b408..38ee2a64e 100644
--- a/src/audio_core/renderer/command/command_generator.h
+++ b/src/audio_core/renderer/command/command_generator.h
@@ -12,7 +12,7 @@
12namespace AudioCore { 12namespace AudioCore {
13struct AudioRendererSystemContext; 13struct AudioRendererSystemContext;
14 14
15namespace AudioRenderer { 15namespace Renderer {
16class CommandBuffer; 16class CommandBuffer;
17struct CommandListHeader; 17struct CommandListHeader;
18class VoiceContext; 18class VoiceContext;
@@ -345,5 +345,5 @@ private:
345 PerformanceManager* performance_manager; 345 PerformanceManager* performance_manager;
346}; 346};
347 347
348} // namespace AudioRenderer 348} // namespace Renderer
349} // namespace AudioCore 349} // namespace AudioCore
diff --git a/src/audio_core/renderer/command/command_list_header.h b/src/audio_core/renderer/command/command_list_header.h
index 988530b1f..de9ee070b 100644
--- a/src/audio_core/renderer/command/command_list_header.h
+++ b/src/audio_core/renderer/command/command_list_header.h
@@ -8,7 +8,7 @@
8#include "audio_core/common/common.h" 8#include "audio_core/common/common.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::Renderer {
12 12
13struct CommandListHeader { 13struct CommandListHeader {
14 u64 buffer_size; 14 u64 buffer_size;
@@ -19,4 +19,4 @@ struct CommandListHeader {
19 u32 sample_rate; 19 u32 sample_rate;
20}; 20};
21 21
22} // namespace AudioCore::AudioRenderer 22} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.cpp b/src/audio_core/renderer/command/command_processing_time_estimator.cpp
index 3091f587a..a48a016b1 100644
--- a/src/audio_core/renderer/command/command_processing_time_estimator.cpp
+++ b/src/audio_core/renderer/command/command_processing_time_estimator.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/command/command_processing_time_estimator.h" 4#include "audio_core/renderer/command/command_processing_time_estimator.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8u32 CommandProcessingTimeEstimatorVersion1::Estimate( 8u32 CommandProcessingTimeEstimatorVersion1::Estimate(
9 const PcmInt16DataSourceVersion1Command& command) const { 9 const PcmInt16DataSourceVersion1Command& command) const {
@@ -3617,4 +3617,4 @@ u32 CommandProcessingTimeEstimatorVersion5::Estimate(const CompressorCommand& co
3617 } 3617 }
3618} 3618}
3619 3619
3620} // namespace AudioCore::AudioRenderer 3620} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.h b/src/audio_core/renderer/command/command_processing_time_estimator.h
index 452217196..1c76e4ba4 100644
--- a/src/audio_core/renderer/command/command_processing_time_estimator.h
+++ b/src/audio_core/renderer/command/command_processing_time_estimator.h
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/command/commands.h" 6#include "audio_core/renderer/command/commands.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10/** 10/**
11 * Estimate the processing time required for all commands. 11 * Estimate the processing time required for all commands.
12 */ 12 */
@@ -251,4 +251,4 @@ private:
251 u32 buffer_count{}; 251 u32 buffer_count{};
252}; 252};
253 253
254} // namespace AudioCore::AudioRenderer 254} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/adpcm.cpp b/src/audio_core/renderer/command/data_source/adpcm.cpp
index e66ed2990..e7f82d3b3 100644
--- a/src/audio_core/renderer/command/data_source/adpcm.cpp
+++ b/src/audio_core/renderer/command/data_source/adpcm.cpp
@@ -3,23 +3,29 @@
3 3
4#include <span> 4#include <span>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/data_source/adpcm.h" 7#include "audio_core/renderer/command/data_source/adpcm.h"
8#include "audio_core/renderer/command/data_source/decode.h" 8#include "audio_core/renderer/command/data_source/decode.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12void AdpcmDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, 12void AdpcmDataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor,
13 std::string& string) { 13 std::string& string) {
14 string += fmt::format("AdpcmDataSourceVersion1Command\n\toutput_index {:02X} source sample " 14 string += fmt::format("AdpcmDataSourceVersion1Command\n\toutput_index {:02X} source sample "
15 "rate {} target sample rate {} src quality {}\n", 15 "rate {} target sample rate {} src quality {}\n",
16 output_index, sample_rate, processor.target_sample_rate, src_quality); 16 output_index, sample_rate, processor.target_sample_rate, src_quality);
17} 17}
18 18
19void AdpcmDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { 19void AdpcmDataSourceVersion1Command::Process(const AudioRenderer::CommandListProcessor& processor) {
20 auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, 20 auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count,
21 processor.sample_count)}; 21 processor.sample_count)};
22 22
23 for (auto& wave_buffer : wave_buffers) {
24 wave_buffer.loop_start_offset = wave_buffer.start_offset;
25 wave_buffer.loop_end_offset = wave_buffer.end_offset;
26 wave_buffer.loop_count = wave_buffer.loop ? -1 : 0;
27 }
28
23 DecodeFromWaveBuffersArgs args{ 29 DecodeFromWaveBuffersArgs args{
24 .sample_format{SampleFormat::Adpcm}, 30 .sample_format{SampleFormat::Adpcm},
25 .output{out_buffer}, 31 .output{out_buffer},
@@ -41,18 +47,18 @@ void AdpcmDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& p
41 DecodeFromWaveBuffers(*processor.memory, args); 47 DecodeFromWaveBuffers(*processor.memory, args);
42} 48}
43 49
44bool AdpcmDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { 50bool AdpcmDataSourceVersion1Command::Verify(const AudioRenderer::CommandListProcessor& processor) {
45 return true; 51 return true;
46} 52}
47 53
48void AdpcmDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, 54void AdpcmDataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor,
49 std::string& string) { 55 std::string& string) {
50 string += fmt::format("AdpcmDataSourceVersion2Command\n\toutput_index {:02X} source sample " 56 string += fmt::format("AdpcmDataSourceVersion2Command\n\toutput_index {:02X} source sample "
51 "rate {} target sample rate {} src quality {}\n", 57 "rate {} target sample rate {} src quality {}\n",
52 output_index, sample_rate, processor.target_sample_rate, src_quality); 58 output_index, sample_rate, processor.target_sample_rate, src_quality);
53} 59}
54 60
55void AdpcmDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { 61void AdpcmDataSourceVersion2Command::Process(const AudioRenderer::CommandListProcessor& processor) {
56 auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count, 62 auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count,
57 processor.sample_count)}; 63 processor.sample_count)};
58 64
@@ -77,8 +83,8 @@ void AdpcmDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& p
77 DecodeFromWaveBuffers(*processor.memory, args); 83 DecodeFromWaveBuffers(*processor.memory, args);
78} 84}
79 85
80bool AdpcmDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { 86bool AdpcmDataSourceVersion2Command::Verify(const AudioRenderer::CommandListProcessor& processor) {
81 return true; 87 return true;
82} 88}
83 89
84} // namespace AudioCore::AudioRenderer 90} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/adpcm.h b/src/audio_core/renderer/command/data_source/adpcm.h
index a9cf9cee4..487846f0c 100644
--- a/src/audio_core/renderer/command/data_source/adpcm.h
+++ b/src/audio_core/renderer/command/data_source/adpcm.h
@@ -11,11 +11,12 @@
11#include "audio_core/renderer/command/icommand.h" 11#include "audio_core/renderer/command/icommand.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13 13
14namespace AudioCore::AudioRenderer { 14namespace AudioCore::ADSP::AudioRenderer {
15namespace ADSP {
16class CommandListProcessor; 15class CommandListProcessor;
17} 16}
18 17
18namespace AudioCore::Renderer {
19
19/** 20/**
20 * AudioRenderer command to decode ADPCM-encoded version 1 wavebuffers 21 * AudioRenderer command to decode ADPCM-encoded version 1 wavebuffers
21 * into the output_index mix buffer. 22 * into the output_index mix buffer.
@@ -27,14 +28,14 @@ struct AdpcmDataSourceVersion1Command : ICommand {
27 * @param processor - The CommandListProcessor processing this command. 28 * @param processor - The CommandListProcessor processing this command.
28 * @param string - The string to print into. 29 * @param string - The string to print into.
29 */ 30 */
30 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 31 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
31 32
32 /** 33 /**
33 * Process this command. 34 * Process this command.
34 * 35 *
35 * @param processor - The CommandListProcessor processing this command. 36 * @param processor - The CommandListProcessor processing this command.
36 */ 37 */
37 void Process(const ADSP::CommandListProcessor& processor) override; 38 void Process(const AudioRenderer::CommandListProcessor& processor) override;
38 39
39 /** 40 /**
40 * Verify this command's data is valid. 41 * Verify this command's data is valid.
@@ -42,13 +43,13 @@ struct AdpcmDataSourceVersion1Command : ICommand {
42 * @param processor - The CommandListProcessor processing this command. 43 * @param processor - The CommandListProcessor processing this command.
43 * @return True if the command is valid, otherwise false. 44 * @return True if the command is valid, otherwise false.
44 */ 45 */
45 bool Verify(const ADSP::CommandListProcessor& processor) override; 46 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
46 47
47 /// Quality used for sample rate conversion 48 /// Quality used for sample rate conversion
48 SrcQuality src_quality; 49 SrcQuality src_quality;
49 /// Mix buffer index for decoded samples 50 /// Mix buffer index for decoded samples
50 s16 output_index; 51 s16 output_index;
51 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) 52 /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags)
52 u16 flags; 53 u16 flags;
53 /// Wavebuffer sample rate 54 /// Wavebuffer sample rate
54 u32 sample_rate; 55 u32 sample_rate;
@@ -75,14 +76,14 @@ struct AdpcmDataSourceVersion2Command : ICommand {
75 * @param processor - The CommandListProcessor processing this command. 76 * @param processor - The CommandListProcessor processing this command.
76 * @param string - The string to print into. 77 * @param string - The string to print into.
77 */ 78 */
78 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 79 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
79 80
80 /** 81 /**
81 * Process this command. 82 * Process this command.
82 * 83 *
83 * @param processor - The CommandListProcessor processing this command. 84 * @param processor - The CommandListProcessor processing this command.
84 */ 85 */
85 void Process(const ADSP::CommandListProcessor& processor) override; 86 void Process(const AudioRenderer::CommandListProcessor& processor) override;
86 87
87 /** 88 /**
88 * Verify this command's data is valid. 89 * Verify this command's data is valid.
@@ -90,13 +91,13 @@ struct AdpcmDataSourceVersion2Command : ICommand {
90 * @param processor - The CommandListProcessor processing this command. 91 * @param processor - The CommandListProcessor processing this command.
91 * @return True if the command is valid, otherwise false. 92 * @return True if the command is valid, otherwise false.
92 */ 93 */
93 bool Verify(const ADSP::CommandListProcessor& processor) override; 94 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
94 95
95 /// Quality used for sample rate conversion 96 /// Quality used for sample rate conversion
96 SrcQuality src_quality; 97 SrcQuality src_quality;
97 /// Mix buffer index for decoded samples 98 /// Mix buffer index for decoded samples
98 s16 output_index; 99 s16 output_index;
99 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) 100 /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags)
100 u16 flags; 101 u16 flags;
101 /// Wavebuffer sample rate 102 /// Wavebuffer sample rate
102 u32 sample_rate; 103 u32 sample_rate;
@@ -116,4 +117,4 @@ struct AdpcmDataSourceVersion2Command : ICommand {
116 u64 data_size; 117 u64 data_size;
117}; 118};
118 119
119} // namespace AudioCore::AudioRenderer 120} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp
index 257aa866e..911dae3c1 100644
--- a/src/audio_core/renderer/command/data_source/decode.cpp
+++ b/src/audio_core/renderer/command/data_source/decode.cpp
@@ -11,7 +11,7 @@
11#include "common/scratch_buffer.h" 11#include "common/scratch_buffer.h"
12#include "core/memory.h" 12#include "core/memory.h"
13 13
14namespace AudioCore::AudioRenderer { 14namespace AudioCore::Renderer {
15 15
16constexpr u32 TempBufferSize = 0x3F00; 16constexpr u32 TempBufferSize = 0x3F00;
17constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4}; 17constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
@@ -123,11 +123,13 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
123 return 0; 123 return 0;
124 } 124 }
125 125
126 auto samples_to_process{ 126 auto start_pos{req.start_offset + req.offset};
127 std::min(req.end_offset - req.start_offset - req.offset, req.samples_to_read)}; 127 auto samples_to_process{std::min(req.end_offset - start_pos, req.samples_to_read)};
128 if (samples_to_process == 0) {
129 return 0;
130 }
128 131
129 auto samples_to_read{samples_to_process}; 132 auto samples_to_read{samples_to_process};
130 auto start_pos{req.start_offset + req.offset};
131 auto samples_remaining_in_frame{start_pos % SamplesPerFrame}; 133 auto samples_remaining_in_frame{start_pos % SamplesPerFrame};
132 auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame + 134 auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame +
133 samples_remaining_in_frame}; 135 samples_remaining_in_frame};
@@ -225,13 +227,24 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
225 * @param args - The wavebuffer data, and information for how to decode it. 227 * @param args - The wavebuffer data, and information for how to decode it.
226 */ 228 */
227void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) { 229void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) {
230 static constexpr auto EndWaveBuffer = [](auto& voice_state, auto& wavebuffer, auto& index,
231 auto& played_samples, auto& consumed) -> void {
232 voice_state.wave_buffer_valid[index] = false;
233 voice_state.loop_count = 0;
234
235 if (wavebuffer.stream_ended) {
236 played_samples = 0;
237 }
238
239 index = (index + 1) % MaxWaveBuffers;
240 consumed++;
241 };
228 auto& voice_state{*args.voice_state}; 242 auto& voice_state{*args.voice_state};
229 auto remaining_sample_count{args.sample_count}; 243 auto remaining_sample_count{args.sample_count};
230 auto fraction{voice_state.fraction}; 244 auto fraction{voice_state.fraction};
231 245
232 const auto sample_rate_ratio{ 246 const auto sample_rate_ratio{Common::FixedPoint<49, 15>(
233 (Common::FixedPoint<49, 15>(args.source_sample_rate) / args.target_sample_rate) * 247 (f32)args.source_sample_rate / (f32)args.target_sample_rate * (f32)args.pitch)};
234 args.pitch};
235 const auto size_required{fraction + remaining_sample_count * sample_rate_ratio}; 248 const auto size_required{fraction + remaining_sample_count * sample_rate_ratio};
236 249
237 if (size_required < 0) { 250 if (size_required < 0) {
@@ -298,22 +311,23 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
298 auto end_offset{wavebuffer.end_offset}; 311 auto end_offset{wavebuffer.end_offset};
299 312
300 if (wavebuffer.loop && voice_state.loop_count > 0 && 313 if (wavebuffer.loop && voice_state.loop_count > 0 &&
301 wavebuffer.loop_start_offset != 0 && wavebuffer.loop_end_offset != 0 &&
302 wavebuffer.loop_start_offset <= wavebuffer.loop_end_offset) { 314 wavebuffer.loop_start_offset <= wavebuffer.loop_end_offset) {
303 start_offset = wavebuffer.loop_start_offset; 315 start_offset = wavebuffer.loop_start_offset;
304 end_offset = wavebuffer.loop_end_offset; 316 end_offset = wavebuffer.loop_end_offset;
305 } 317 }
306 318
307 DecodeArg decode_arg{.buffer{wavebuffer.buffer}, 319 DecodeArg decode_arg{
308 .buffer_size{wavebuffer.buffer_size}, 320 .buffer{wavebuffer.buffer},
309 .start_offset{start_offset}, 321 .buffer_size{wavebuffer.buffer_size},
310 .end_offset{end_offset}, 322 .start_offset{start_offset},
311 .channel_count{args.channel_count}, 323 .end_offset{end_offset},
312 .coefficients{}, 324 .channel_count{args.channel_count},
313 .adpcm_context{nullptr}, 325 .coefficients{},
314 .target_channel{args.channel}, 326 .adpcm_context{nullptr},
315 .offset{offset}, 327 .target_channel{args.channel},
316 .samples_to_read{samples_to_read - samples_read}}; 328 .offset{offset},
329 .samples_to_read{samples_to_read - samples_read},
330 };
317 331
318 s32 samples_decoded{0}; 332 s32 samples_decoded{0};
319 333
@@ -350,42 +364,30 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
350 temp_buffer_pos += samples_decoded; 364 temp_buffer_pos += samples_decoded;
351 offset += samples_decoded; 365 offset += samples_decoded;
352 366
353 if (samples_decoded == 0 || offset >= end_offset - start_offset) { 367 if (samples_decoded && offset < end_offset - start_offset) {
354 offset = 0; 368 continue;
355 if (!wavebuffer.loop) { 369 }
356 voice_state.wave_buffer_valid[wavebuffer_index] = false; 370
357 voice_state.loop_count = 0; 371 offset = 0;
358 372 if (wavebuffer.loop) {
359 if (wavebuffer.stream_ended) { 373 voice_state.loop_count++;
360 played_sample_count = 0; 374 if (wavebuffer.loop_count >= 0 &&
361 } 375 (voice_state.loop_count > wavebuffer.loop_count || samples_decoded == 0)) {
362 376 EndWaveBuffer(voice_state, wavebuffer, wavebuffer_index, played_sample_count,
363 wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers; 377 wavebuffers_consumed);
364 wavebuffers_consumed++; 378 }
365 } else { 379
366 voice_state.loop_count++; 380 if (samples_decoded == 0) {
367 if (wavebuffer.loop_count > 0 && 381 is_buffer_starved = true;
368 (voice_state.loop_count > wavebuffer.loop_count || samples_decoded == 0)) { 382 break;
369 voice_state.wave_buffer_valid[wavebuffer_index] = false; 383 }
370 voice_state.loop_count = 0; 384
371 385 if (args.IsVoicePlayedSampleCountResetAtLoopPointSupported) {
372 if (wavebuffer.stream_ended) { 386 played_sample_count = 0;
373 played_sample_count = 0;
374 }
375
376 wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers;
377 wavebuffers_consumed++;
378 }
379
380 if (samples_decoded == 0) {
381 is_buffer_starved = true;
382 break;
383 }
384
385 if (args.IsVoicePlayedSampleCountResetAtLoopPointSupported) {
386 played_sample_count = 0;
387 }
388 } 387 }
388 } else {
389 EndWaveBuffer(voice_state, wavebuffer, wavebuffer_index, played_sample_count,
390 wavebuffers_consumed);
389 } 391 }
390 } 392 }
391 393
@@ -423,4 +425,4 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
423 voice_state.fraction = fraction; 425 voice_state.fraction = fraction;
424} 426}
425 427
426} // namespace AudioCore::AudioRenderer 428} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/decode.h b/src/audio_core/renderer/command/data_source/decode.h
index 4d63d6fa8..5f52f32f0 100644
--- a/src/audio_core/renderer/command/data_source/decode.h
+++ b/src/audio_core/renderer/command/data_source/decode.h
@@ -15,7 +15,7 @@ namespace Core::Memory {
15class Memory; 15class Memory;
16} 16}
17 17
18namespace AudioCore::AudioRenderer { 18namespace AudioCore::Renderer {
19 19
20struct DecodeFromWaveBuffersArgs { 20struct DecodeFromWaveBuffersArgs {
21 SampleFormat sample_format; 21 SampleFormat sample_format;
@@ -56,4 +56,4 @@ struct DecodeArg {
56 */ 56 */
57void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args); 57void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args);
58 58
59} // namespace AudioCore::AudioRenderer 59} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_float.cpp b/src/audio_core/renderer/command/data_source/pcm_float.cpp
index be77fab69..d1f685656 100644
--- a/src/audio_core/renderer/command/data_source/pcm_float.cpp
+++ b/src/audio_core/renderer/command/data_source/pcm_float.cpp
@@ -1,13 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/data_source/decode.h" 5#include "audio_core/renderer/command/data_source/decode.h"
6#include "audio_core/renderer/command/data_source/pcm_float.h" 6#include "audio_core/renderer/command/data_source/pcm_float.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10void PcmFloatDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, 10void PcmFloatDataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor,
11 std::string& string) { 11 std::string& string) {
12 string += 12 string +=
13 fmt::format("PcmFloatDataSourceVersion1Command\n\toutput_index {:02X} channel {} " 13 fmt::format("PcmFloatDataSourceVersion1Command\n\toutput_index {:02X} channel {} "
@@ -16,10 +16,17 @@ void PcmFloatDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& p
16 processor.target_sample_rate, src_quality); 16 processor.target_sample_rate, src_quality);
17} 17}
18 18
19void PcmFloatDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { 19void PcmFloatDataSourceVersion1Command::Process(
20 const AudioRenderer::CommandListProcessor& processor) {
20 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, 21 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
21 processor.sample_count); 22 processor.sample_count);
22 23
24 for (auto& wave_buffer : wave_buffers) {
25 wave_buffer.loop_start_offset = wave_buffer.start_offset;
26 wave_buffer.loop_end_offset = wave_buffer.end_offset;
27 wave_buffer.loop_count = wave_buffer.loop ? -1 : 0;
28 }
29
23 DecodeFromWaveBuffersArgs args{ 30 DecodeFromWaveBuffersArgs args{
24 .sample_format{SampleFormat::PcmFloat}, 31 .sample_format{SampleFormat::PcmFloat},
25 .output{out_buffer}, 32 .output{out_buffer},
@@ -41,11 +48,12 @@ void PcmFloatDataSourceVersion1Command::Process(const ADSP::CommandListProcessor
41 DecodeFromWaveBuffers(*processor.memory, args); 48 DecodeFromWaveBuffers(*processor.memory, args);
42} 49}
43 50
44bool PcmFloatDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { 51bool PcmFloatDataSourceVersion1Command::Verify(
52 const AudioRenderer::CommandListProcessor& processor) {
45 return true; 53 return true;
46} 54}
47 55
48void PcmFloatDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, 56void PcmFloatDataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor,
49 std::string& string) { 57 std::string& string) {
50 string += 58 string +=
51 fmt::format("PcmFloatDataSourceVersion2Command\n\toutput_index {:02X} channel {} " 59 fmt::format("PcmFloatDataSourceVersion2Command\n\toutput_index {:02X} channel {} "
@@ -54,7 +62,8 @@ void PcmFloatDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& p
54 processor.target_sample_rate, src_quality); 62 processor.target_sample_rate, src_quality);
55} 63}
56 64
57void PcmFloatDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { 65void PcmFloatDataSourceVersion2Command::Process(
66 const AudioRenderer::CommandListProcessor& processor) {
58 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, 67 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
59 processor.sample_count); 68 processor.sample_count);
60 69
@@ -79,8 +88,9 @@ void PcmFloatDataSourceVersion2Command::Process(const ADSP::CommandListProcessor
79 DecodeFromWaveBuffers(*processor.memory, args); 88 DecodeFromWaveBuffers(*processor.memory, args);
80} 89}
81 90
82bool PcmFloatDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { 91bool PcmFloatDataSourceVersion2Command::Verify(
92 const AudioRenderer::CommandListProcessor& processor) {
83 return true; 93 return true;
84} 94}
85 95
86} // namespace AudioCore::AudioRenderer 96} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_float.h b/src/audio_core/renderer/command/data_source/pcm_float.h
index e4af77c20..2c9d1877e 100644
--- a/src/audio_core/renderer/command/data_source/pcm_float.h
+++ b/src/audio_core/renderer/command/data_source/pcm_float.h
@@ -9,11 +9,12 @@
9#include "audio_core/renderer/command/icommand.h" 9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::ADSP::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor; 13class CommandListProcessor;
15} 14}
16 15
16namespace AudioCore::Renderer {
17
17/** 18/**
18 * AudioRenderer command to decode PCM float-encoded version 1 wavebuffers 19 * AudioRenderer command to decode PCM float-encoded version 1 wavebuffers
19 * into the output_index mix buffer. 20 * into the output_index mix buffer.
@@ -25,14 +26,14 @@ struct PcmFloatDataSourceVersion1Command : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,13 +41,13 @@ struct PcmFloatDataSourceVersion1Command : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// Quality used for sample rate conversion 46 /// Quality used for sample rate conversion
46 SrcQuality src_quality; 47 SrcQuality src_quality;
47 /// Mix buffer index for decoded samples 48 /// Mix buffer index for decoded samples
48 s16 output_index; 49 s16 output_index;
49 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) 50 /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags)
50 u16 flags; 51 u16 flags;
51 /// Wavebuffer sample rate 52 /// Wavebuffer sample rate
52 u32 sample_rate; 53 u32 sample_rate;
@@ -73,14 +74,14 @@ struct PcmFloatDataSourceVersion2Command : ICommand {
73 * @param processor - The CommandListProcessor processing this command. 74 * @param processor - The CommandListProcessor processing this command.
74 * @param string - The string to print into. 75 * @param string - The string to print into.
75 */ 76 */
76 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 77 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
77 78
78 /** 79 /**
79 * Process this command. 80 * Process this command.
80 * 81 *
81 * @param processor - The CommandListProcessor processing this command. 82 * @param processor - The CommandListProcessor processing this command.
82 */ 83 */
83 void Process(const ADSP::CommandListProcessor& processor) override; 84 void Process(const AudioRenderer::CommandListProcessor& processor) override;
84 85
85 /** 86 /**
86 * Verify this command's data is valid. 87 * Verify this command's data is valid.
@@ -88,13 +89,13 @@ struct PcmFloatDataSourceVersion2Command : ICommand {
88 * @param processor - The CommandListProcessor processing this command. 89 * @param processor - The CommandListProcessor processing this command.
89 * @return True if the command is valid, otherwise false. 90 * @return True if the command is valid, otherwise false.
90 */ 91 */
91 bool Verify(const ADSP::CommandListProcessor& processor) override; 92 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
92 93
93 /// Quality used for sample rate conversion 94 /// Quality used for sample rate conversion
94 SrcQuality src_quality; 95 SrcQuality src_quality;
95 /// Mix buffer index for decoded samples 96 /// Mix buffer index for decoded samples
96 s16 output_index; 97 s16 output_index;
97 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) 98 /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags)
98 u16 flags; 99 u16 flags;
99 /// Wavebuffer sample rate 100 /// Wavebuffer sample rate
100 u32 sample_rate; 101 u32 sample_rate;
@@ -110,4 +111,4 @@ struct PcmFloatDataSourceVersion2Command : ICommand {
110 CpuAddr voice_state; 111 CpuAddr voice_state;
111}; 112};
112 113
113} // namespace AudioCore::AudioRenderer 114} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.cpp b/src/audio_core/renderer/command/data_source/pcm_int16.cpp
index 7a27463e4..c89a5aaac 100644
--- a/src/audio_core/renderer/command/data_source/pcm_int16.cpp
+++ b/src/audio_core/renderer/command/data_source/pcm_int16.cpp
@@ -3,13 +3,13 @@
3 3
4#include <span> 4#include <span>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/data_source/decode.h" 7#include "audio_core/renderer/command/data_source/decode.h"
8#include "audio_core/renderer/command/data_source/pcm_int16.h" 8#include "audio_core/renderer/command/data_source/pcm_int16.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12void PcmInt16DataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor, 12void PcmInt16DataSourceVersion1Command::Dump(const AudioRenderer::CommandListProcessor& processor,
13 std::string& string) { 13 std::string& string) {
14 string += 14 string +=
15 fmt::format("PcmInt16DataSourceVersion1Command\n\toutput_index {:02X} channel {} " 15 fmt::format("PcmInt16DataSourceVersion1Command\n\toutput_index {:02X} channel {} "
@@ -18,10 +18,17 @@ void PcmInt16DataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& p
18 processor.target_sample_rate, src_quality); 18 processor.target_sample_rate, src_quality);
19} 19}
20 20
21void PcmInt16DataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) { 21void PcmInt16DataSourceVersion1Command::Process(
22 const AudioRenderer::CommandListProcessor& processor) {
22 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, 23 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
23 processor.sample_count); 24 processor.sample_count);
24 25
26 for (auto& wave_buffer : wave_buffers) {
27 wave_buffer.loop_start_offset = wave_buffer.start_offset;
28 wave_buffer.loop_end_offset = wave_buffer.end_offset;
29 wave_buffer.loop_count = wave_buffer.loop ? -1 : 0;
30 }
31
25 DecodeFromWaveBuffersArgs args{ 32 DecodeFromWaveBuffersArgs args{
26 .sample_format{SampleFormat::PcmInt16}, 33 .sample_format{SampleFormat::PcmInt16},
27 .output{out_buffer}, 34 .output{out_buffer},
@@ -43,11 +50,12 @@ void PcmInt16DataSourceVersion1Command::Process(const ADSP::CommandListProcessor
43 DecodeFromWaveBuffers(*processor.memory, args); 50 DecodeFromWaveBuffers(*processor.memory, args);
44} 51}
45 52
46bool PcmInt16DataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { 53bool PcmInt16DataSourceVersion1Command::Verify(
54 const AudioRenderer::CommandListProcessor& processor) {
47 return true; 55 return true;
48} 56}
49 57
50void PcmInt16DataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor, 58void PcmInt16DataSourceVersion2Command::Dump(const AudioRenderer::CommandListProcessor& processor,
51 std::string& string) { 59 std::string& string) {
52 string += 60 string +=
53 fmt::format("PcmInt16DataSourceVersion2Command\n\toutput_index {:02X} channel {} " 61 fmt::format("PcmInt16DataSourceVersion2Command\n\toutput_index {:02X} channel {} "
@@ -56,7 +64,8 @@ void PcmInt16DataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& p
56 processor.target_sample_rate, src_quality); 64 processor.target_sample_rate, src_quality);
57} 65}
58 66
59void PcmInt16DataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) { 67void PcmInt16DataSourceVersion2Command::Process(
68 const AudioRenderer::CommandListProcessor& processor) {
60 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count, 69 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
61 processor.sample_count); 70 processor.sample_count);
62 DecodeFromWaveBuffersArgs args{ 71 DecodeFromWaveBuffersArgs args{
@@ -80,8 +89,9 @@ void PcmInt16DataSourceVersion2Command::Process(const ADSP::CommandListProcessor
80 DecodeFromWaveBuffers(*processor.memory, args); 89 DecodeFromWaveBuffers(*processor.memory, args);
81} 90}
82 91
83bool PcmInt16DataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { 92bool PcmInt16DataSourceVersion2Command::Verify(
93 const AudioRenderer::CommandListProcessor& processor) {
84 return true; 94 return true;
85} 95}
86 96
87} // namespace AudioCore::AudioRenderer 97} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.h b/src/audio_core/renderer/command/data_source/pcm_int16.h
index 5de1ad60d..2c013f003 100644
--- a/src/audio_core/renderer/command/data_source/pcm_int16.h
+++ b/src/audio_core/renderer/command/data_source/pcm_int16.h
@@ -9,11 +9,12 @@
9#include "audio_core/renderer/command/icommand.h" 9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::ADSP::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor; 13class CommandListProcessor;
15} 14}
16 15
16namespace AudioCore::Renderer {
17
17/** 18/**
18 * AudioRenderer command to decode PCM s16-encoded version 1 wavebuffers 19 * AudioRenderer command to decode PCM s16-encoded version 1 wavebuffers
19 * into the output_index mix buffer. 20 * into the output_index mix buffer.
@@ -25,14 +26,14 @@ struct PcmInt16DataSourceVersion1Command : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,13 +41,13 @@ struct PcmInt16DataSourceVersion1Command : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// Quality used for sample rate conversion 46 /// Quality used for sample rate conversion
46 SrcQuality src_quality; 47 SrcQuality src_quality;
47 /// Mix buffer index for decoded samples 48 /// Mix buffer index for decoded samples
48 s16 output_index; 49 s16 output_index;
49 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) 50 /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags)
50 u16 flags; 51 u16 flags;
51 /// Wavebuffer sample rate 52 /// Wavebuffer sample rate
52 u32 sample_rate; 53 u32 sample_rate;
@@ -72,26 +73,26 @@ struct PcmInt16DataSourceVersion2Command : ICommand {
72 * @param processor - The CommandListProcessor processing this command. 73 * @param processor - The CommandListProcessor processing this command.
73 * @param string - The string to print into. 74 * @param string - The string to print into.
74 */ 75 */
75 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 76 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
76 77
77 /** 78 /**
78 * Process this command. 79 * Process this command.
79 * @param processor - The CommandListProcessor processing this command. 80 * @param processor - The CommandListProcessor processing this command.
80 */ 81 */
81 void Process(const ADSP::CommandListProcessor& processor) override; 82 void Process(const AudioRenderer::CommandListProcessor& processor) override;
82 83
83 /** 84 /**
84 * Verify this command's data is valid. 85 * Verify this command's data is valid.
85 * @param processor - The CommandListProcessor processing this command. 86 * @param processor - The CommandListProcessor processing this command.
86 * @return True if the command is valid, otherwise false. 87 * @return True if the command is valid, otherwise false.
87 */ 88 */
88 bool Verify(const ADSP::CommandListProcessor& processor) override; 89 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
89 90
90 /// Quality used for sample rate conversion 91 /// Quality used for sample rate conversion
91 SrcQuality src_quality; 92 SrcQuality src_quality;
92 /// Mix buffer index for decoded samples 93 /// Mix buffer index for decoded samples
93 s16 output_index; 94 s16 output_index;
94 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags) 95 /// Flags to control decoding (see AudioCore::Renderer::VoiceInfo::Flags)
95 u16 flags; 96 u16 flags;
96 /// Wavebuffer sample rate 97 /// Wavebuffer sample rate
97 u32 sample_rate; 98 u32 sample_rate;
@@ -107,4 +108,4 @@ struct PcmInt16DataSourceVersion2Command : ICommand {
107 CpuAddr voice_state; 108 CpuAddr voice_state;
108}; 109};
109 110
110} // namespace AudioCore::AudioRenderer 111} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/aux_.cpp b/src/audio_core/renderer/command/effect/aux_.cpp
index a3e12b3e7..74d9c229f 100644
--- a/src/audio_core/renderer/command/effect/aux_.cpp
+++ b/src/audio_core/renderer/command/effect/aux_.cpp
@@ -1,13 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/effect/aux_.h" 5#include "audio_core/renderer/command/effect/aux_.h"
6#include "audio_core/renderer/effect/aux_.h" 6#include "audio_core/renderer/effect/aux_.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/memory.h" 8#include "core/memory.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11/** 11/**
12 * Reset an AuxBuffer. 12 * Reset an AuxBuffer.
13 * 13 *
@@ -175,13 +175,13 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_,
175 return read_count_; 175 return read_count_;
176} 176}
177 177
178void AuxCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 178void AuxCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
179 std::string& string) { 179 std::string& string) {
180 string += fmt::format("AuxCommand\n\tenabled {} input {:02X} output {:02X}\n", effect_enabled, 180 string += fmt::format("AuxCommand\n\tenabled {} input {:02X} output {:02X}\n", effect_enabled,
181 input, output); 181 input, output);
182} 182}
183 183
184void AuxCommand::Process(const ADSP::CommandListProcessor& processor) { 184void AuxCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
185 auto input_buffer{ 185 auto input_buffer{
186 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; 186 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)};
187 auto output_buffer{ 187 auto output_buffer{
@@ -208,8 +208,8 @@ void AuxCommand::Process(const ADSP::CommandListProcessor& processor) {
208 } 208 }
209} 209}
210 210
211bool AuxCommand::Verify(const ADSP::CommandListProcessor& processor) { 211bool AuxCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
212 return true; 212 return true;
213} 213}
214 214
215} // namespace AudioCore::AudioRenderer 215} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/aux_.h b/src/audio_core/renderer/command/effect/aux_.h
index 825c93732..da1e55261 100644
--- a/src/audio_core/renderer/command/effect/aux_.h
+++ b/src/audio_core/renderer/command/effect/aux_.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command to read and write an auxiliary buffer, writing the input mix buffer to game 18 * AudioRenderer command to read and write an auxiliary buffer, writing the input mix buffer to game
18 * memory, and reading into the output buffer from game memory. 19 * memory, and reading into the output buffer from game memory.
@@ -24,14 +25,14 @@ struct AuxCommand : ICommand {
24 * @param processor - The CommandListProcessor processing this command. 25 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into. 26 * @param string - The string to print into.
26 */ 27 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 28 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
28 29
29 /** 30 /**
30 * Process this command. 31 * Process this command.
31 * 32 *
32 * @param processor - The CommandListProcessor processing this command. 33 * @param processor - The CommandListProcessor processing this command.
33 */ 34 */
34 void Process(const ADSP::CommandListProcessor& processor) override; 35 void Process(const AudioRenderer::CommandListProcessor& processor) override;
35 36
36 /** 37 /**
37 * Verify this command's data is valid. 38 * Verify this command's data is valid.
@@ -39,7 +40,7 @@ struct AuxCommand : ICommand {
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false. 41 * @return True if the command is valid, otherwise false.
41 */ 42 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override; 43 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
43 44
44 /// Input mix buffer index 45 /// Input mix buffer index
45 s16 input; 46 s16 input;
@@ -63,4 +64,4 @@ struct AuxCommand : ICommand {
63 bool effect_enabled; 64 bool effect_enabled;
64}; 65};
65 66
66} // namespace AudioCore::AudioRenderer 67} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp
index dea6423dc..3392e7747 100644
--- a/src/audio_core/renderer/command/effect/biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp
@@ -1,12 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/effect/biquad_filter.h" 5#include "audio_core/renderer/command/effect/biquad_filter.h"
6#include "audio_core/renderer/voice/voice_state.h" 6#include "audio_core/renderer/voice/voice_state.h"
7#include "common/bit_cast.h" 7#include "common/bit_cast.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10/** 10/**
11 * Biquad filter float implementation. 11 * Biquad filter float implementation.
12 * 12 *
@@ -76,14 +76,14 @@ static void ApplyBiquadFilterInt(std::span<s32> output, std::span<const s32> inp
76 } 76 }
77} 77}
78 78
79void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 79void BiquadFilterCommand::Dump(
80 std::string& string) { 80 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
81 string += fmt::format( 81 string += fmt::format(
82 "BiquadFilterCommand\n\tinput {:02X} output {:02X} needs_init {} use_float_processing {}\n", 82 "BiquadFilterCommand\n\tinput {:02X} output {:02X} needs_init {} use_float_processing {}\n",
83 input, output, needs_init, use_float_processing); 83 input, output, needs_init, use_float_processing);
84} 84}
85 85
86void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { 86void BiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
87 auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)}; 87 auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)};
88 if (needs_init) { 88 if (needs_init) {
89 *state_ = {}; 89 *state_ = {};
@@ -103,8 +103,8 @@ void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) {
103 } 103 }
104} 104}
105 105
106bool BiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) { 106bool BiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
107 return true; 107 return true;
108} 108}
109 109
110} // namespace AudioCore::AudioRenderer 110} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.h b/src/audio_core/renderer/command/effect/biquad_filter.h
index 4c9c42d29..0e903930a 100644
--- a/src/audio_core/renderer/command/effect/biquad_filter.h
+++ b/src/audio_core/renderer/command/effect/biquad_filter.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/voice/voice_state.h" 10#include "audio_core/renderer/voice/voice_state.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for applying a biquad filter to the input mix buffer, saving the results to 20 * AudioRenderer command for applying a biquad filter to the input mix buffer, saving the results to
20 * the output mix buffer. 21 * the output mix buffer.
@@ -26,14 +27,14 @@ struct BiquadFilterCommand : ICommand {
26 * @param processor - The CommandListProcessor processing this command. 27 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into. 28 * @param string - The string to print into.
28 */ 29 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 30 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
30 31
31 /** 32 /**
32 * Process this command. 33 * Process this command.
33 * 34 *
34 * @param processor - The CommandListProcessor processing this command. 35 * @param processor - The CommandListProcessor processing this command.
35 */ 36 */
36 void Process(const ADSP::CommandListProcessor& processor) override; 37 void Process(const AudioRenderer::CommandListProcessor& processor) override;
37 38
38 /** 39 /**
39 * Verify this command's data is valid. 40 * Verify this command's data is valid.
@@ -41,7 +42,7 @@ struct BiquadFilterCommand : ICommand {
41 * @param processor - The CommandListProcessor processing this command. 42 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false. 43 * @return True if the command is valid, otherwise false.
43 */ 44 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override; 45 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
45 46
46 /// Input mix buffer index 47 /// Input mix buffer index
47 s16 input; 48 s16 input;
@@ -71,4 +72,4 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
71 std::array<s16, 3>& b, std::array<s16, 2>& a, 72 std::array<s16, 3>& b, std::array<s16, 2>& a,
72 VoiceState::BiquadFilterState& state, const u32 sample_count); 73 VoiceState::BiquadFilterState& state, const u32 sample_count);
73 74
74} // namespace AudioCore::AudioRenderer 75} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/capture.cpp b/src/audio_core/renderer/command/effect/capture.cpp
index 042fd286e..f235ce027 100644
--- a/src/audio_core/renderer/command/effect/capture.cpp
+++ b/src/audio_core/renderer/command/effect/capture.cpp
@@ -1,12 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/effect/capture.h" 5#include "audio_core/renderer/command/effect/capture.h"
6#include "audio_core/renderer/effect/aux_.h" 6#include "audio_core/renderer/effect/aux_.h"
7#include "core/memory.h" 7#include "core/memory.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10/** 10/**
11 * Reset an AuxBuffer. 11 * Reset an AuxBuffer.
12 * 12 *
@@ -118,13 +118,13 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_in
118 return write_count_; 118 return write_count_;
119} 119}
120 120
121void CaptureCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 121void CaptureCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
122 std::string& string) { 122 std::string& string) {
123 string += fmt::format("CaptureCommand\n\tenabled {} input {:02X} output {:02X}", effect_enabled, 123 string += fmt::format("CaptureCommand\n\tenabled {} input {:02X} output {:02X}", effect_enabled,
124 input, output); 124 input, output);
125} 125}
126 126
127void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) { 127void CaptureCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
128 if (effect_enabled) { 128 if (effect_enabled) {
129 auto input_buffer{ 129 auto input_buffer{
130 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)}; 130 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)};
@@ -135,8 +135,8 @@ void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) {
135 } 135 }
136} 136}
137 137
138bool CaptureCommand::Verify(const ADSP::CommandListProcessor& processor) { 138bool CaptureCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
139 return true; 139 return true;
140} 140}
141 141
142} // namespace AudioCore::AudioRenderer 142} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/capture.h b/src/audio_core/renderer/command/effect/capture.h
index 8670acb24..a0016c6f6 100644
--- a/src/audio_core/renderer/command/effect/capture.h
+++ b/src/audio_core/renderer/command/effect/capture.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for capturing a mix buffer. That is, writing it back to a given game memory 18 * AudioRenderer command for capturing a mix buffer. That is, writing it back to a given game memory
18 * address. 19 * address.
@@ -24,14 +25,14 @@ struct CaptureCommand : ICommand {
24 * @param processor - The CommandListProcessor processing this command. 25 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into. 26 * @param string - The string to print into.
26 */ 27 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 28 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
28 29
29 /** 30 /**
30 * Process this command. 31 * Process this command.
31 * 32 *
32 * @param processor - The CommandListProcessor processing this command. 33 * @param processor - The CommandListProcessor processing this command.
33 */ 34 */
34 void Process(const ADSP::CommandListProcessor& processor) override; 35 void Process(const AudioRenderer::CommandListProcessor& processor) override;
35 36
36 /** 37 /**
37 * Verify this command's data is valid. 38 * Verify this command's data is valid.
@@ -39,7 +40,7 @@ struct CaptureCommand : ICommand {
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false. 41 * @return True if the command is valid, otherwise false.
41 */ 42 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override; 43 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
43 44
44 /// Input mix buffer index 45 /// Input mix buffer index
45 s16 input; 46 s16 input;
@@ -59,4 +60,4 @@ struct CaptureCommand : ICommand {
59 bool effect_enabled; 60 bool effect_enabled;
60}; 61};
61 62
62} // namespace AudioCore::AudioRenderer 63} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp
index ee9b68d5b..7ff707f4e 100644
--- a/src/audio_core/renderer/command/effect/compressor.cpp
+++ b/src/audio_core/renderer/command/effect/compressor.cpp
@@ -5,11 +5,11 @@
5#include <span> 5#include <span>
6#include <vector> 6#include <vector>
7 7
8#include "audio_core/renderer/adsp/command_list_processor.h" 8#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
9#include "audio_core/renderer/command/effect/compressor.h" 9#include "audio_core/renderer/command/effect/compressor.h"
10#include "audio_core/renderer/effect/compressor.h" 10#include "audio_core/renderer/effect/compressor.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13 13
14static void SetCompressorEffectParameter(const CompressorInfo::ParameterVersion2& params, 14static void SetCompressorEffectParameter(const CompressorInfo::ParameterVersion2& params,
15 CompressorInfo::State& state) { 15 CompressorInfo::State& state) {
@@ -110,7 +110,7 @@ static void ApplyCompressorEffect(const CompressorInfo::ParameterVersion2& param
110 } 110 }
111} 111}
112 112
113void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 113void CompressorCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
114 std::string& string) { 114 std::string& string) {
115 string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled); 115 string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled);
116 for (s16 i = 0; i < parameter.channel_count; i++) { 116 for (s16 i = 0; i < parameter.channel_count; i++) {
@@ -123,7 +123,7 @@ void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
123 string += "\n"; 123 string += "\n";
124} 124}
125 125
126void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { 126void CompressorCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
127 std::array<std::span<const s32>, MaxChannels> input_buffers{}; 127 std::array<std::span<const s32>, MaxChannels> input_buffers{};
128 std::array<std::span<s32>, MaxChannels> output_buffers{}; 128 std::array<std::span<s32>, MaxChannels> output_buffers{};
129 129
@@ -148,8 +148,8 @@ void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
148 processor.sample_count); 148 processor.sample_count);
149} 149}
150 150
151bool CompressorCommand::Verify(const ADSP::CommandListProcessor& processor) { 151bool CompressorCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
152 return true; 152 return true;
153} 153}
154 154
155} // namespace AudioCore::AudioRenderer 155} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/compressor.h b/src/audio_core/renderer/command/effect/compressor.h
index f8e96cb43..c011aa927 100644
--- a/src/audio_core/renderer/command/effect/compressor.h
+++ b/src/audio_core/renderer/command/effect/compressor.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/effect/compressor.h" 10#include "audio_core/renderer/effect/compressor.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for limiting volume between a high and low threshold. 20 * AudioRenderer command for limiting volume between a high and low threshold.
20 * Version 1. 21 * Version 1.
@@ -26,14 +27,14 @@ struct CompressorCommand : ICommand {
26 * @param processor - The CommandListProcessor processing this command. 27 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into. 28 * @param string - The string to print into.
28 */ 29 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 30 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
30 31
31 /** 32 /**
32 * Process this command. 33 * Process this command.
33 * 34 *
34 * @param processor - The CommandListProcessor processing this command. 35 * @param processor - The CommandListProcessor processing this command.
35 */ 36 */
36 void Process(const ADSP::CommandListProcessor& processor) override; 37 void Process(const AudioRenderer::CommandListProcessor& processor) override;
37 38
38 /** 39 /**
39 * Verify this command's data is valid. 40 * Verify this command's data is valid.
@@ -41,7 +42,7 @@ struct CompressorCommand : ICommand {
41 * @param processor - The CommandListProcessor processing this command. 42 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false. 43 * @return True if the command is valid, otherwise false.
43 */ 44 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override; 45 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
45 46
46 /// Input mix buffer offsets for each channel 47 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs; 48 std::array<s16, MaxChannels> inputs;
@@ -57,4 +58,4 @@ struct CompressorCommand : ICommand {
57 bool effect_enabled; 58 bool effect_enabled;
58}; 59};
59 60
60} // namespace AudioCore::AudioRenderer 61} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp
index e536cbb1e..ffb298c07 100644
--- a/src/audio_core/renderer/command/effect/delay.cpp
+++ b/src/audio_core/renderer/command/effect/delay.cpp
@@ -1,10 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/effect/delay.h" 5#include "audio_core/renderer/command/effect/delay.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8/** 8/**
9 * Update the DelayInfo state according to the given parameters. 9 * Update the DelayInfo state according to the given parameters.
10 * 10 *
@@ -194,7 +194,7 @@ static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayIn
194 } 194 }
195} 195}
196 196
197void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 197void DelayCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
198 std::string& string) { 198 std::string& string) {
199 string += fmt::format("DelayCommand\n\tenabled {} \n\tinputs: ", effect_enabled); 199 string += fmt::format("DelayCommand\n\tenabled {} \n\tinputs: ", effect_enabled);
200 for (u32 i = 0; i < MaxChannels; i++) { 200 for (u32 i = 0; i < MaxChannels; i++) {
@@ -207,7 +207,7 @@ void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proce
207 string += "\n"; 207 string += "\n";
208} 208}
209 209
210void DelayCommand::Process(const ADSP::CommandListProcessor& processor) { 210void DelayCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
211 std::array<std::span<const s32>, MaxChannels> input_buffers{}; 211 std::array<std::span<const s32>, MaxChannels> input_buffers{};
212 std::array<std::span<s32>, MaxChannels> output_buffers{}; 212 std::array<std::span<s32>, MaxChannels> output_buffers{};
213 213
@@ -231,8 +231,8 @@ void DelayCommand::Process(const ADSP::CommandListProcessor& processor) {
231 processor.sample_count); 231 processor.sample_count);
232} 232}
233 233
234bool DelayCommand::Verify(const ADSP::CommandListProcessor& processor) { 234bool DelayCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
235 return true; 235 return true;
236} 236}
237 237
238} // namespace AudioCore::AudioRenderer 238} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/delay.h b/src/audio_core/renderer/command/effect/delay.h
index b7a15ae6b..bfeac7af4 100644
--- a/src/audio_core/renderer/command/effect/delay.h
+++ b/src/audio_core/renderer/command/effect/delay.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/effect/delay.h" 10#include "audio_core/renderer/effect/delay.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for a delay effect. Delays inputs mix buffers according to the parameters 20 * AudioRenderer command for a delay effect. Delays inputs mix buffers according to the parameters
20 * and state, outputs receives the delayed samples. 21 * and state, outputs receives the delayed samples.
@@ -26,14 +27,14 @@ struct DelayCommand : ICommand {
26 * @param processor - The CommandListProcessor processing this command. 27 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into. 28 * @param string - The string to print into.
28 */ 29 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 30 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
30 31
31 /** 32 /**
32 * Process this command. 33 * Process this command.
33 * 34 *
34 * @param processor - The CommandListProcessor processing this command. 35 * @param processor - The CommandListProcessor processing this command.
35 */ 36 */
36 void Process(const ADSP::CommandListProcessor& processor) override; 37 void Process(const AudioRenderer::CommandListProcessor& processor) override;
37 38
38 /** 39 /**
39 * Verify this command's data is valid. 40 * Verify this command's data is valid.
@@ -41,7 +42,7 @@ struct DelayCommand : ICommand {
41 * @param processor - The CommandListProcessor processing this command. 42 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false. 43 * @return True if the command is valid, otherwise false.
43 */ 44 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override; 45 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
45 46
46 /// Input mix buffer offsets for each channel 47 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs; 48 std::array<s16, MaxChannels> inputs;
@@ -57,4 +58,4 @@ struct DelayCommand : ICommand {
57 bool effect_enabled; 58 bool effect_enabled;
58}; 59};
59 60
60} // namespace AudioCore::AudioRenderer 61} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
index d2bfb67cc..ecfdfabc6 100644
--- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
@@ -3,11 +3,11 @@
3 3
4#include <numbers> 4#include <numbers>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/effect/i3dl2_reverb.h" 7#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
8#include "common/polyfill_ranges.h" 8#include "common/polyfill_ranges.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> MinDelayLineTimes{ 12constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> MinDelayLineTimes{
13 5.0f, 13 5.0f,
@@ -394,7 +394,7 @@ static void ApplyI3dl2ReverbEffect(const I3dl2ReverbInfo::ParameterVersion1& par
394 } 394 }
395} 395}
396 396
397void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 397void I3dl2ReverbCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
398 std::string& string) { 398 std::string& string) {
399 string += fmt::format("I3dl2ReverbCommand\n\tenabled {} \n\tinputs: ", effect_enabled); 399 string += fmt::format("I3dl2ReverbCommand\n\tenabled {} \n\tinputs: ", effect_enabled);
400 for (u32 i = 0; i < parameter.channel_count; i++) { 400 for (u32 i = 0; i < parameter.channel_count; i++) {
@@ -407,7 +407,7 @@ void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
407 string += "\n"; 407 string += "\n";
408} 408}
409 409
410void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { 410void I3dl2ReverbCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
411 std::array<std::span<const s32>, MaxChannels> input_buffers{}; 411 std::array<std::span<const s32>, MaxChannels> input_buffers{};
412 std::array<std::span<s32>, MaxChannels> output_buffers{}; 412 std::array<std::span<s32>, MaxChannels> output_buffers{};
413 413
@@ -431,8 +431,8 @@ void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
431 processor.sample_count); 431 processor.sample_count);
432} 432}
433 433
434bool I3dl2ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) { 434bool I3dl2ReverbCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
435 return true; 435 return true;
436} 436}
437 437
438} // namespace AudioCore::AudioRenderer 438} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.h b/src/audio_core/renderer/command/effect/i3dl2_reverb.h
index 243877056..e4c538ae8 100644
--- a/src/audio_core/renderer/command/effect/i3dl2_reverb.h
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/effect/i3dl2.h" 10#include "audio_core/renderer/effect/i3dl2.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for a I3DL2Reverb effect. Apply a reverb to inputs mix buffer according to 20 * AudioRenderer command for a I3DL2Reverb effect. Apply a reverb to inputs mix buffer according to
20 * the I3DL2 spec, outputs receives the results. 21 * the I3DL2 spec, outputs receives the results.
@@ -26,14 +27,14 @@ struct I3dl2ReverbCommand : ICommand {
26 * @param processor - The CommandListProcessor processing this command. 27 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into. 28 * @param string - The string to print into.
28 */ 29 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 30 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
30 31
31 /** 32 /**
32 * Process this command. 33 * Process this command.
33 * 34 *
34 * @param processor - The CommandListProcessor processing this command. 35 * @param processor - The CommandListProcessor processing this command.
35 */ 36 */
36 void Process(const ADSP::CommandListProcessor& processor) override; 37 void Process(const AudioRenderer::CommandListProcessor& processor) override;
37 38
38 /** 39 /**
39 * Verify this command's data is valid. 40 * Verify this command's data is valid.
@@ -41,7 +42,7 @@ struct I3dl2ReverbCommand : ICommand {
41 * @param processor - The CommandListProcessor processing this command. 42 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false. 43 * @return True if the command is valid, otherwise false.
43 */ 44 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override; 45 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
45 46
46 /// Input mix buffer offsets for each channel 47 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs; 48 std::array<s16, MaxChannels> inputs;
@@ -57,4 +58,4 @@ struct I3dl2ReverbCommand : ICommand {
57 bool effect_enabled; 58 bool effect_enabled;
58}; 59};
59 60
60} // namespace AudioCore::AudioRenderer 61} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp
index 4161a9821..63aa06f5c 100644
--- a/src/audio_core/renderer/command/effect/light_limiter.cpp
+++ b/src/audio_core/renderer/command/effect/light_limiter.cpp
@@ -1,10 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/effect/light_limiter.h" 5#include "audio_core/renderer/command/effect/light_limiter.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8/** 8/**
9 * Update the LightLimiterInfo state according to the given parameters. 9 * Update the LightLimiterInfo state according to the given parameters.
10 * A no-op. 10 * A no-op.
@@ -133,8 +133,8 @@ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& p
133 } 133 }
134} 134}
135 135
136void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 136void LightLimiterVersion1Command::Dump(
137 std::string& string) { 137 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
138 string += fmt::format("LightLimiterVersion1Command\n\tinputs: "); 138 string += fmt::format("LightLimiterVersion1Command\n\tinputs: ");
139 for (u32 i = 0; i < MaxChannels; i++) { 139 for (u32 i = 0; i < MaxChannels; i++) {
140 string += fmt::format("{:02X}, ", inputs[i]); 140 string += fmt::format("{:02X}, ", inputs[i]);
@@ -146,7 +146,7 @@ void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListP
146 string += "\n"; 146 string += "\n";
147} 147}
148 148
149void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) { 149void LightLimiterVersion1Command::Process(const AudioRenderer::CommandListProcessor& processor) {
150 std::array<std::span<const s32>, MaxChannels> input_buffers{}; 150 std::array<std::span<const s32>, MaxChannels> input_buffers{};
151 std::array<std::span<s32>, MaxChannels> output_buffers{}; 151 std::array<std::span<s32>, MaxChannels> output_buffers{};
152 152
@@ -172,12 +172,12 @@ void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& proc
172 processor.sample_count, statistics); 172 processor.sample_count, statistics);
173} 173}
174 174
175bool LightLimiterVersion1Command::Verify(const ADSP::CommandListProcessor& processor) { 175bool LightLimiterVersion1Command::Verify(const AudioRenderer::CommandListProcessor& processor) {
176 return true; 176 return true;
177} 177}
178 178
179void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 179void LightLimiterVersion2Command::Dump(
180 std::string& string) { 180 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
181 string += fmt::format("LightLimiterVersion2Command\n\tinputs: \n"); 181 string += fmt::format("LightLimiterVersion2Command\n\tinputs: \n");
182 for (u32 i = 0; i < MaxChannels; i++) { 182 for (u32 i = 0; i < MaxChannels; i++) {
183 string += fmt::format("{:02X}, ", inputs[i]); 183 string += fmt::format("{:02X}, ", inputs[i]);
@@ -189,7 +189,7 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP
189 string += "\n"; 189 string += "\n";
190} 190}
191 191
192void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { 192void LightLimiterVersion2Command::Process(const AudioRenderer::CommandListProcessor& processor) {
193 std::array<std::span<const s32>, MaxChannels> input_buffers{}; 193 std::array<std::span<const s32>, MaxChannels> input_buffers{};
194 std::array<std::span<s32>, MaxChannels> output_buffers{}; 194 std::array<std::span<s32>, MaxChannels> output_buffers{};
195 195
@@ -215,8 +215,8 @@ void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& proc
215 processor.sample_count, statistics); 215 processor.sample_count, statistics);
216} 216}
217 217
218bool LightLimiterVersion2Command::Verify(const ADSP::CommandListProcessor& processor) { 218bool LightLimiterVersion2Command::Verify(const AudioRenderer::CommandListProcessor& processor) {
219 return true; 219 return true;
220} 220}
221 221
222} // namespace AudioCore::AudioRenderer 222} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/light_limiter.h b/src/audio_core/renderer/command/effect/light_limiter.h
index 5d98272c7..6e3ee1b53 100644
--- a/src/audio_core/renderer/command/effect/light_limiter.h
+++ b/src/audio_core/renderer/command/effect/light_limiter.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/effect/light_limiter.h" 10#include "audio_core/renderer/effect/light_limiter.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for limiting volume between a high and low threshold. 20 * AudioRenderer command for limiting volume between a high and low threshold.
20 * Version 1. 21 * Version 1.
@@ -26,14 +27,14 @@ struct LightLimiterVersion1Command : ICommand {
26 * @param processor - The CommandListProcessor processing this command. 27 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into. 28 * @param string - The string to print into.
28 */ 29 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 30 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
30 31
31 /** 32 /**
32 * Process this command. 33 * Process this command.
33 * 34 *
34 * @param processor - The CommandListProcessor processing this command. 35 * @param processor - The CommandListProcessor processing this command.
35 */ 36 */
36 void Process(const ADSP::CommandListProcessor& processor) override; 37 void Process(const AudioRenderer::CommandListProcessor& processor) override;
37 38
38 /** 39 /**
39 * Verify this command's data is valid. 40 * Verify this command's data is valid.
@@ -41,7 +42,7 @@ struct LightLimiterVersion1Command : ICommand {
41 * @param processor - The CommandListProcessor processing this command. 42 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false. 43 * @return True if the command is valid, otherwise false.
43 */ 44 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override; 45 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
45 46
46 /// Input mix buffer offsets for each channel 47 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs; 48 std::array<s16, MaxChannels> inputs;
@@ -68,21 +69,21 @@ struct LightLimiterVersion2Command : ICommand {
68 * @param processor - The CommandListProcessor processing this command. 69 * @param processor - The CommandListProcessor processing this command.
69 * @param string - The string to print into. 70 * @param string - The string to print into.
70 */ 71 */
71 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 72 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
72 73
73 /** 74 /**
74 * Process this command. 75 * Process this command.
75 * 76 *
76 * @param processor - The CommandListProcessor processing this command. 77 * @param processor - The CommandListProcessor processing this command.
77 */ 78 */
78 void Process(const ADSP::CommandListProcessor& processor) override; 79 void Process(const AudioRenderer::CommandListProcessor& processor) override;
79 80
80 /** 81 /**
81 * Verify this command's data is valid. 82 * Verify this command's data is valid.
82 * 83 *
83 * @param processor - The CommandListProcessor processing this command. 84 * @param processor - The CommandListProcessor processing this command.
84 */ 85 */
85 bool Verify(const ADSP::CommandListProcessor& processor) override; 86 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
86 87
87 /// Input mix buffer offsets for each channel 88 /// Input mix buffer offsets for each channel
88 std::array<s16, MaxChannels> inputs; 89 std::array<s16, MaxChannels> inputs;
@@ -100,4 +101,4 @@ struct LightLimiterVersion2Command : ICommand {
100 bool effect_enabled; 101 bool effect_enabled;
101}; 102};
102 103
103} // namespace AudioCore::AudioRenderer 104} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
index 48a7cba8a..208bbeaf2 100644
--- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
@@ -1,20 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/effect/biquad_filter.h" 5#include "audio_core/renderer/command/effect/biquad_filter.h"
6#include "audio_core/renderer/command/effect/multi_tap_biquad_filter.h" 6#include "audio_core/renderer/command/effect/multi_tap_biquad_filter.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10void MultiTapBiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 10void MultiTapBiquadFilterCommand::Dump(
11 std::string& string) { 11 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
12 string += fmt::format( 12 string += fmt::format(
13 "MultiTapBiquadFilterCommand\n\tinput {:02X}\n\toutput {:02X}\n\tneeds_init ({}, {})\n", 13 "MultiTapBiquadFilterCommand\n\tinput {:02X}\n\toutput {:02X}\n\tneeds_init ({}, {})\n",
14 input, output, needs_init[0], needs_init[1]); 14 input, output, needs_init[0], needs_init[1]);
15} 15}
16 16
17void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) { 17void MultiTapBiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
18 if (filter_tap_count > MaxBiquadFilters) { 18 if (filter_tap_count > MaxBiquadFilters) {
19 LOG_ERROR(Service_Audio, "Too many filter taps! {}", filter_tap_count); 19 LOG_ERROR(Service_Audio, "Too many filter taps! {}", filter_tap_count);
20 filter_tap_count = MaxBiquadFilters; 20 filter_tap_count = MaxBiquadFilters;
@@ -38,8 +38,8 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc
38 } 38 }
39} 39}
40 40
41bool MultiTapBiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) { 41bool MultiTapBiquadFilterCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
42 return true; 42 return true;
43} 43}
44 44
45} // namespace AudioCore::AudioRenderer 45} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h
index 99c2c0830..50fce80b0 100644
--- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h
+++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/voice/voice_info.h" 10#include "audio_core/renderer/voice/voice_info.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for applying multiple biquads at once. 20 * AudioRenderer command for applying multiple biquads at once.
20 */ 21 */
@@ -25,14 +26,14 @@ struct MultiTapBiquadFilterCommand : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,7 +41,7 @@ struct MultiTapBiquadFilterCommand : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// Input mix buffer index 46 /// Input mix buffer index
46 s16 input; 47 s16 input;
@@ -56,4 +57,4 @@ struct MultiTapBiquadFilterCommand : ICommand {
56 u8 filter_tap_count; 57 u8 filter_tap_count;
57}; 58};
58 59
59} // namespace AudioCore::AudioRenderer 60} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp
index fc2f15a5e..7f152a962 100644
--- a/src/audio_core/renderer/command/effect/reverb.cpp
+++ b/src/audio_core/renderer/command/effect/reverb.cpp
@@ -4,11 +4,11 @@
4#include <numbers> 4#include <numbers>
5#include <ranges> 5#include <ranges>
6 6
7#include "audio_core/renderer/adsp/command_list_processor.h" 7#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
8#include "audio_core/renderer/command/effect/reverb.h" 8#include "audio_core/renderer/command/effect/reverb.h"
9#include "common/polyfill_ranges.h" 9#include "common/polyfill_ranges.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::Renderer {
12 12
13constexpr std::array<f32, ReverbInfo::MaxDelayLines> FdnMaxDelayLineTimes = { 13constexpr std::array<f32, ReverbInfo::MaxDelayLines> FdnMaxDelayLineTimes = {
14 53.9532470703125f, 14 53.9532470703125f,
@@ -396,7 +396,7 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever
396 } 396 }
397} 397}
398 398
399void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 399void ReverbCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
400 std::string& string) { 400 std::string& string) {
401 string += fmt::format( 401 string += fmt::format(
402 "ReverbCommand\n\tenabled {} long_size_pre_delay_supported {}\n\tinputs: ", effect_enabled, 402 "ReverbCommand\n\tenabled {} long_size_pre_delay_supported {}\n\tinputs: ", effect_enabled,
@@ -411,7 +411,7 @@ void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc
411 string += "\n"; 411 string += "\n";
412} 412}
413 413
414void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) { 414void ReverbCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
415 std::array<std::span<const s32>, MaxChannels> input_buffers{}; 415 std::array<std::span<const s32>, MaxChannels> input_buffers{};
416 std::array<std::span<s32>, MaxChannels> output_buffers{}; 416 std::array<std::span<s32>, MaxChannels> output_buffers{};
417 417
@@ -435,8 +435,8 @@ void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
435 processor.sample_count); 435 processor.sample_count);
436} 436}
437 437
438bool ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) { 438bool ReverbCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
439 return true; 439 return true;
440} 440}
441 441
442} // namespace AudioCore::AudioRenderer 442} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/effect/reverb.h b/src/audio_core/renderer/command/effect/reverb.h
index 328756150..2056c73f2 100644
--- a/src/audio_core/renderer/command/effect/reverb.h
+++ b/src/audio_core/renderer/command/effect/reverb.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/effect/reverb.h" 10#include "audio_core/renderer/effect/reverb.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for a Reverb effect. Apply a reverb to inputs mix buffer, outputs receives 20 * AudioRenderer command for a Reverb effect. Apply a reverb to inputs mix buffer, outputs receives
20 * the results. 21 * the results.
@@ -26,14 +27,14 @@ struct ReverbCommand : ICommand {
26 * @param processor - The CommandListProcessor processing this command. 27 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into. 28 * @param string - The string to print into.
28 */ 29 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 30 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
30 31
31 /** 32 /**
32 * Process this command. 33 * Process this command.
33 * 34 *
34 * @param processor - The CommandListProcessor processing this command. 35 * @param processor - The CommandListProcessor processing this command.
35 */ 36 */
36 void Process(const ADSP::CommandListProcessor& processor) override; 37 void Process(const AudioRenderer::CommandListProcessor& processor) override;
37 38
38 /** 39 /**
39 * Verify this command's data is valid. 40 * Verify this command's data is valid.
@@ -41,7 +42,7 @@ struct ReverbCommand : ICommand {
41 * @param processor - The CommandListProcessor processing this command. 42 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false. 43 * @return True if the command is valid, otherwise false.
43 */ 44 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override; 45 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
45 46
46 /// Input mix buffer offsets for each channel 47 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs; 48 std::array<s16, MaxChannels> inputs;
@@ -59,4 +60,4 @@ struct ReverbCommand : ICommand {
59 bool long_size_pre_delay_supported; 60 bool long_size_pre_delay_supported;
60}; 61};
61 62
62} // namespace AudioCore::AudioRenderer 63} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/icommand.h b/src/audio_core/renderer/command/icommand.h
index f2dd41254..10a78ddf2 100644
--- a/src/audio_core/renderer/command/icommand.h
+++ b/src/audio_core/renderer/command/icommand.h
@@ -3,14 +3,18 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <string>
7
6#include "audio_core/common/common.h" 8#include "audio_core/common/common.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
9namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
10namespace ADSP {
11class CommandListProcessor; 12class CommandListProcessor;
12} 13}
13 14
15namespace AudioCore::Renderer {
16using namespace ::AudioCore::ADSP;
17
14enum class CommandId : u8 { 18enum class CommandId : u8 {
15 /* 0x00 */ Invalid, 19 /* 0x00 */ Invalid,
16 /* 0x01 */ DataSourcePcmInt16Version1, 20 /* 0x01 */ DataSourcePcmInt16Version1,
@@ -59,14 +63,15 @@ struct ICommand {
59 * @param processor - The CommandListProcessor processing this command. 63 * @param processor - The CommandListProcessor processing this command.
60 * @param string - The string to print into. 64 * @param string - The string to print into.
61 */ 65 */
62 virtual void Dump(const ADSP::CommandListProcessor& processor, std::string& string) = 0; 66 virtual void Dump(const AudioRenderer::CommandListProcessor& processor,
67 std::string& string) = 0;
63 68
64 /** 69 /**
65 * Process this command. 70 * Process this command.
66 * 71 *
67 * @param processor - The CommandListProcessor processing this command. 72 * @param processor - The CommandListProcessor processing this command.
68 */ 73 */
69 virtual void Process(const ADSP::CommandListProcessor& processor) = 0; 74 virtual void Process(const AudioRenderer::CommandListProcessor& processor) = 0;
70 75
71 /** 76 /**
72 * Verify this command's data is valid. 77 * Verify this command's data is valid.
@@ -74,7 +79,7 @@ struct ICommand {
74 * @param processor - The CommandListProcessor processing this command. 79 * @param processor - The CommandListProcessor processing this command.
75 * @return True if the command is valid, otherwise false. 80 * @return True if the command is valid, otherwise false.
76 */ 81 */
77 virtual bool Verify(const ADSP::CommandListProcessor& processor) = 0; 82 virtual bool Verify(const AudioRenderer::CommandListProcessor& processor) = 0;
78 83
79 /// Command magic 0xCAFEBABE 84 /// Command magic 0xCAFEBABE
80 u32 magic{}; 85 u32 magic{};
@@ -90,4 +95,4 @@ struct ICommand {
90 u32 node_id{}; 95 u32 node_id{};
91}; 96};
92 97
93} // namespace AudioCore::AudioRenderer 98} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/clear_mix.cpp b/src/audio_core/renderer/command/mix/clear_mix.cpp
index 4f649d6a8..060d7cb28 100644
--- a/src/audio_core/renderer/command/mix/clear_mix.cpp
+++ b/src/audio_core/renderer/command/mix/clear_mix.cpp
@@ -3,22 +3,22 @@
3 3
4#include <string> 4#include <string>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/mix/clear_mix.h" 7#include "audio_core/renderer/command/mix/clear_mix.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11void ClearMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 11void ClearMixBufferCommand::Dump(
12 std::string& string) { 12 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
13 string += fmt::format("ClearMixBufferCommand\n"); 13 string += fmt::format("ClearMixBufferCommand\n");
14} 14}
15 15
16void ClearMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) { 16void ClearMixBufferCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
17 memset(processor.mix_buffers.data(), 0, processor.mix_buffers.size_bytes()); 17 memset(processor.mix_buffers.data(), 0, processor.mix_buffers.size_bytes());
18} 18}
19 19
20bool ClearMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) { 20bool ClearMixBufferCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
21 return true; 21 return true;
22} 22}
23 23
24} // namespace AudioCore::AudioRenderer 24} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/clear_mix.h b/src/audio_core/renderer/command/mix/clear_mix.h
index 956ec0b65..650fa1a8a 100644
--- a/src/audio_core/renderer/command/mix/clear_mix.h
+++ b/src/audio_core/renderer/command/mix/clear_mix.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for a clearing the mix buffers. 18 * AudioRenderer command for a clearing the mix buffers.
18 * Used at the start of each command list. 19 * Used at the start of each command list.
@@ -24,14 +25,14 @@ struct ClearMixBufferCommand : ICommand {
24 * @param processor - The CommandListProcessor processing this command. 25 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into. 26 * @param string - The string to print into.
26 */ 27 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 28 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
28 29
29 /** 30 /**
30 * Process this command. 31 * Process this command.
31 * 32 *
32 * @param processor - The CommandListProcessor processing this command. 33 * @param processor - The CommandListProcessor processing this command.
33 */ 34 */
34 void Process(const ADSP::CommandListProcessor& processor) override; 35 void Process(const AudioRenderer::CommandListProcessor& processor) override;
35 36
36 /** 37 /**
37 * Verify this command's data is valid. 38 * Verify this command's data is valid.
@@ -39,7 +40,7 @@ struct ClearMixBufferCommand : ICommand {
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false. 41 * @return True if the command is valid, otherwise false.
41 */ 42 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override; 43 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
43}; 44};
44 45
45} // namespace AudioCore::AudioRenderer 46} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/copy_mix.cpp b/src/audio_core/renderer/command/mix/copy_mix.cpp
index 1d49f1644..5d386f95a 100644
--- a/src/audio_core/renderer/command/mix/copy_mix.cpp
+++ b/src/audio_core/renderer/command/mix/copy_mix.cpp
@@ -1,18 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/mix/copy_mix.h" 5#include "audio_core/renderer/command/mix/copy_mix.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8 8
9void CopyMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 9void CopyMixBufferCommand::Dump(
10 std::string& string) { 10 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
11 string += fmt::format("CopyMixBufferCommand\n\tinput {:02X} output {:02X}\n", input_index, 11 string += fmt::format("CopyMixBufferCommand\n\tinput {:02X} output {:02X}\n", input_index,
12 output_index); 12 output_index);
13} 13}
14 14
15void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) { 15void CopyMixBufferCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
16 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, 16 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
17 processor.sample_count)}; 17 processor.sample_count)};
18 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, 18 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
@@ -20,8 +20,8 @@ void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor)
20 std::memcpy(output.data(), input.data(), processor.sample_count * sizeof(s32)); 20 std::memcpy(output.data(), input.data(), processor.sample_count * sizeof(s32));
21} 21}
22 22
23bool CopyMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) { 23bool CopyMixBufferCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
24 return true; 24 return true;
25} 25}
26 26
27} // namespace AudioCore::AudioRenderer 27} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/copy_mix.h b/src/audio_core/renderer/command/mix/copy_mix.h
index a59007fb6..ae247c3f8 100644
--- a/src/audio_core/renderer/command/mix/copy_mix.h
+++ b/src/audio_core/renderer/command/mix/copy_mix.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for a copying a mix buffer from input to output. 18 * AudioRenderer command for a copying a mix buffer from input to output.
18 */ 19 */
@@ -23,14 +24,14 @@ struct CopyMixBufferCommand : ICommand {
23 * @param processor - The CommandListProcessor processing this command. 24 * @param processor - The CommandListProcessor processing this command.
24 * @param string - The string to print into. 25 * @param string - The string to print into.
25 */ 26 */
26 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 27 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
27 28
28 /** 29 /**
29 * Process this command. 30 * Process this command.
30 * 31 *
31 * @param processor - The CommandListProcessor processing this command. 32 * @param processor - The CommandListProcessor processing this command.
32 */ 33 */
33 void Process(const ADSP::CommandListProcessor& processor) override; 34 void Process(const AudioRenderer::CommandListProcessor& processor) override;
34 35
35 /** 36 /**
36 * Verify this command's data is valid. 37 * Verify this command's data is valid.
@@ -38,7 +39,7 @@ struct CopyMixBufferCommand : ICommand {
38 * @param processor - The CommandListProcessor processing this command. 39 * @param processor - The CommandListProcessor processing this command.
39 * @return True if the command is valid, otherwise false. 40 * @return True if the command is valid, otherwise false.
40 */ 41 */
41 bool Verify(const ADSP::CommandListProcessor& processor) override; 42 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
42 43
43 /// Input mix buffer index 44 /// Input mix buffer index
44 s16 input_index; 45 s16 input_index;
@@ -46,4 +47,4 @@ struct CopyMixBufferCommand : ICommand {
46 s16 output_index; 47 s16 output_index;
47}; 48};
48 49
49} // namespace AudioCore::AudioRenderer 50} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp
index c2bc10061..caedb56b7 100644
--- a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp
+++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp
@@ -1,11 +1,11 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
4#include "audio_core/common/common.h" 5#include "audio_core/common/common.h"
5#include "audio_core/renderer/adsp/command_list_processor.h"
6#include "audio_core/renderer/command/mix/depop_for_mix_buffers.h" 6#include "audio_core/renderer/command/mix/depop_for_mix_buffers.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9/** 9/**
10 * Apply depopping. Add the depopped sample to each incoming new sample, decaying it each time 10 * Apply depopping. Add the depopped sample to each incoming new sample, decaying it each time
11 * according to decay. 11 * according to decay.
@@ -36,13 +36,13 @@ static s32 ApplyDepopMix(std::span<s32> output, const s32 depop_sample,
36 } 36 }
37} 37}
38 38
39void DepopForMixBuffersCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 39void DepopForMixBuffersCommand::Dump(
40 std::string& string) { 40 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
41 string += fmt::format("DepopForMixBuffersCommand\n\tinput {:02X} count {} decay {}\n", input, 41 string += fmt::format("DepopForMixBuffersCommand\n\tinput {:02X} count {} decay {}\n", input,
42 count, decay.to_float()); 42 count, decay.to_float());
43} 43}
44 44
45void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& processor) { 45void DepopForMixBuffersCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
46 auto end_index{std::min(processor.buffer_count, input + count)}; 46 auto end_index{std::min(processor.buffer_count, input + count)};
47 std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index}; 47 std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index};
48 48
@@ -57,8 +57,8 @@ void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& proces
57 } 57 }
58} 58}
59 59
60bool DepopForMixBuffersCommand::Verify(const ADSP::CommandListProcessor& processor) { 60bool DepopForMixBuffersCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
61 return true; 61 return true;
62} 62}
63 63
64} // namespace AudioCore::AudioRenderer 64} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h
index e7268ff27..699d38988 100644
--- a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h
+++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h
@@ -9,11 +9,12 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/fixed_point.h" 10#include "common/fixed_point.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::ADSP::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor; 13class CommandListProcessor;
15} 14}
16 15
16namespace AudioCore::Renderer {
17
17/** 18/**
18 * AudioRenderer command for depopping a mix buffer. 19 * AudioRenderer command for depopping a mix buffer.
19 * Adds a cumulation of previous samples to the current mix buffer with a decay. 20 * Adds a cumulation of previous samples to the current mix buffer with a decay.
@@ -25,14 +26,14 @@ struct DepopForMixBuffersCommand : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,7 +41,7 @@ struct DepopForMixBuffersCommand : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// Starting input mix buffer index 46 /// Starting input mix buffer index
46 u32 input; 47 u32 input;
@@ -52,4 +53,4 @@ struct DepopForMixBuffersCommand : ICommand {
52 CpuAddr depop_buffer; 53 CpuAddr depop_buffer;
53}; 54};
54 55
55} // namespace AudioCore::AudioRenderer 56} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/depop_prepare.cpp b/src/audio_core/renderer/command/mix/depop_prepare.cpp
index 69bb78ccc..2faf4681a 100644
--- a/src/audio_core/renderer/command/mix/depop_prepare.cpp
+++ b/src/audio_core/renderer/command/mix/depop_prepare.cpp
@@ -1,15 +1,15 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/mix/depop_prepare.h" 5#include "audio_core/renderer/command/mix/depop_prepare.h"
6#include "audio_core/renderer/voice/voice_state.h" 6#include "audio_core/renderer/voice/voice_state.h"
7#include "common/fixed_point.h" 7#include "common/fixed_point.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 11void DepopPrepareCommand::Dump(
12 std::string& string) { 12 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
13 string += fmt::format("DepopPrepareCommand\n\tinputs: "); 13 string += fmt::format("DepopPrepareCommand\n\tinputs: ");
14 for (u32 i = 0; i < buffer_count; i++) { 14 for (u32 i = 0; i < buffer_count; i++) {
15 string += fmt::format("{:02X}, ", inputs[i]); 15 string += fmt::format("{:02X}, ", inputs[i]);
@@ -17,7 +17,7 @@ void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor
17 string += "\n"; 17 string += "\n";
18} 18}
19 19
20void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) { 20void DepopPrepareCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
21 auto samples{reinterpret_cast<s32*>(previous_samples)}; 21 auto samples{reinterpret_cast<s32*>(previous_samples)};
22 auto buffer{reinterpret_cast<s32*>(depop_buffer)}; 22 auto buffer{reinterpret_cast<s32*>(depop_buffer)};
23 23
@@ -29,8 +29,8 @@ void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) {
29 } 29 }
30} 30}
31 31
32bool DepopPrepareCommand::Verify(const ADSP::CommandListProcessor& processor) { 32bool DepopPrepareCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
33 return true; 33 return true;
34} 34}
35 35
36} // namespace AudioCore::AudioRenderer 36} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/depop_prepare.h b/src/audio_core/renderer/command/mix/depop_prepare.h
index a5465da9a..161a94461 100644
--- a/src/audio_core/renderer/command/mix/depop_prepare.h
+++ b/src/audio_core/renderer/command/mix/depop_prepare.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for preparing depop. 18 * AudioRenderer command for preparing depop.
18 * Adds the previusly output last samples to the depop buffer. 19 * Adds the previusly output last samples to the depop buffer.
@@ -24,14 +25,14 @@ struct DepopPrepareCommand : ICommand {
24 * @param processor - The CommandListProcessor processing this command. 25 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into. 26 * @param string - The string to print into.
26 */ 27 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 28 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
28 29
29 /** 30 /**
30 * Process this command. 31 * Process this command.
31 * 32 *
32 * @param processor - The CommandListProcessor processing this command. 33 * @param processor - The CommandListProcessor processing this command.
33 */ 34 */
34 void Process(const ADSP::CommandListProcessor& processor) override; 35 void Process(const AudioRenderer::CommandListProcessor& processor) override;
35 36
36 /** 37 /**
37 * Verify this command's data is valid. 38 * Verify this command's data is valid.
@@ -39,7 +40,7 @@ struct DepopPrepareCommand : ICommand {
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false. 41 * @return True if the command is valid, otherwise false.
41 */ 42 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override; 43 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
43 44
44 /// Depop buffer offset for each mix buffer 45 /// Depop buffer offset for each mix buffer
45 std::array<s16, MaxMixBuffers> inputs; 46 std::array<s16, MaxMixBuffers> inputs;
@@ -51,4 +52,4 @@ struct DepopPrepareCommand : ICommand {
51 CpuAddr depop_buffer; 52 CpuAddr depop_buffer;
52}; 53};
53 54
54} // namespace AudioCore::AudioRenderer 55} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/mix.cpp b/src/audio_core/renderer/command/mix/mix.cpp
index 8ecf9b05a..8bd689b88 100644
--- a/src/audio_core/renderer/command/mix/mix.cpp
+++ b/src/audio_core/renderer/command/mix/mix.cpp
@@ -5,11 +5,11 @@
5#include <limits> 5#include <limits>
6#include <span> 6#include <span>
7 7
8#include "audio_core/renderer/adsp/command_list_processor.h" 8#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
9#include "audio_core/renderer/command/mix/mix.h" 9#include "audio_core/renderer/command/mix/mix.h"
10#include "common/fixed_point.h" 10#include "common/fixed_point.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13/** 13/**
14 * Mix input mix buffer into output mix buffer, with volume applied to the input. 14 * Mix input mix buffer into output mix buffer, with volume applied to the input.
15 * 15 *
@@ -28,7 +28,7 @@ static void ApplyMix(std::span<s32> output, std::span<const s32> input, const f3
28 } 28 }
29} 29}
30 30
31void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 31void MixCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
32 std::string& string) { 32 std::string& string) {
33 string += fmt::format("MixCommand"); 33 string += fmt::format("MixCommand");
34 string += fmt::format("\n\tinput {:02X}", input_index); 34 string += fmt::format("\n\tinput {:02X}", input_index);
@@ -37,7 +37,7 @@ void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& process
37 string += "\n"; 37 string += "\n";
38} 38}
39 39
40void MixCommand::Process(const ADSP::CommandListProcessor& processor) { 40void MixCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
41 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, 41 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
42 processor.sample_count)}; 42 processor.sample_count)};
43 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, 43 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
@@ -63,8 +63,8 @@ void MixCommand::Process(const ADSP::CommandListProcessor& processor) {
63 } 63 }
64} 64}
65 65
66bool MixCommand::Verify(const ADSP::CommandListProcessor& processor) { 66bool MixCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
67 return true; 67 return true;
68} 68}
69 69
70} // namespace AudioCore::AudioRenderer 70} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/mix.h b/src/audio_core/renderer/command/mix/mix.h
index 0201cf171..64c812382 100644
--- a/src/audio_core/renderer/command/mix/mix.h
+++ b/src/audio_core/renderer/command/mix/mix.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume 18 * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume
18 * applied to the input. 19 * applied to the input.
@@ -24,14 +25,14 @@ struct MixCommand : ICommand {
24 * @param processor - The CommandListProcessor processing this command. 25 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into. 26 * @param string - The string to print into.
26 */ 27 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 28 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
28 29
29 /** 30 /**
30 * Process this command. 31 * Process this command.
31 * 32 *
32 * @param processor - The CommandListProcessor processing this command. 33 * @param processor - The CommandListProcessor processing this command.
33 */ 34 */
34 void Process(const ADSP::CommandListProcessor& processor) override; 35 void Process(const AudioRenderer::CommandListProcessor& processor) override;
35 36
36 /** 37 /**
37 * Verify this command's data is valid. 38 * Verify this command's data is valid.
@@ -39,7 +40,7 @@ struct MixCommand : ICommand {
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false. 41 * @return True if the command is valid, otherwise false.
41 */ 42 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override; 43 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
43 44
44 /// Fixed point precision 45 /// Fixed point precision
45 u8 precision; 46 u8 precision;
@@ -51,4 +52,4 @@ struct MixCommand : ICommand {
51 f32 volume; 52 f32 volume;
52}; 53};
53 54
54} // namespace AudioCore::AudioRenderer 55} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.cpp b/src/audio_core/renderer/command/mix/mix_ramp.cpp
index d67123cd8..2f6500da5 100644
--- a/src/audio_core/renderer/command/mix/mix_ramp.cpp
+++ b/src/audio_core/renderer/command/mix/mix_ramp.cpp
@@ -1,12 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/mix/mix_ramp.h" 5#include "audio_core/renderer/command/mix/mix_ramp.h"
6#include "common/fixed_point.h" 6#include "common/fixed_point.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11template <size_t Q> 11template <size_t Q>
12s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_, 12s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_,
@@ -33,7 +33,8 @@ s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 vo
33template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, f32, f32, u32); 33template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, f32, f32, u32);
34template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, f32, f32, u32); 34template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, f32, f32, u32);
35 35
36void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { 36void MixRampCommand::Dump(const AudioRenderer::CommandListProcessor& processor,
37 std::string& string) {
37 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; 38 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
38 string += fmt::format("MixRampCommand"); 39 string += fmt::format("MixRampCommand");
39 string += fmt::format("\n\tinput {:02X}", input_index); 40 string += fmt::format("\n\tinput {:02X}", input_index);
@@ -44,7 +45,7 @@ void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::stri
44 string += "\n"; 45 string += "\n";
45} 46}
46 47
47void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) { 48void MixRampCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
48 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, 49 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
49 processor.sample_count)}; 50 processor.sample_count)};
50 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, 51 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
@@ -75,8 +76,8 @@ void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) {
75 } 76 }
76} 77}
77 78
78bool MixRampCommand::Verify(const ADSP::CommandListProcessor& processor) { 79bool MixRampCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
79 return true; 80 return true;
80} 81}
81 82
82} // namespace AudioCore::AudioRenderer 83} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.h b/src/audio_core/renderer/command/mix/mix_ramp.h
index 52f74a273..92209b53a 100644
--- a/src/audio_core/renderer/command/mix/mix_ramp.h
+++ b/src/audio_core/renderer/command/mix/mix_ramp.h
@@ -9,11 +9,12 @@
9#include "audio_core/renderer/command/icommand.h" 9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::ADSP::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor; 13class CommandListProcessor;
15} 14}
16 15
16namespace AudioCore::Renderer {
17
17/** 18/**
18 * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume 19 * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume
19 * applied to the input, and volume ramping to smooth out the transition. 20 * applied to the input, and volume ramping to smooth out the transition.
@@ -25,14 +26,14 @@ struct MixRampCommand : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,7 +41,7 @@ struct MixRampCommand : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// Fixed point precision 46 /// Fixed point precision
46 u8 precision; 47 u8 precision;
@@ -70,4 +71,4 @@ template <size_t Q>
70s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, f32 volume_, f32 ramp_, 71s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, f32 volume_, f32 ramp_,
71 u32 sample_count); 72 u32 sample_count);
72 73
73} // namespace AudioCore::AudioRenderer 74} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp
index 43dbef9fc..64138a9bf 100644
--- a/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp
+++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp
@@ -1,13 +1,14 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/mix/mix_ramp.h" 5#include "audio_core/renderer/command/mix/mix_ramp.h"
6#include "audio_core/renderer/command/mix/mix_ramp_grouped.h" 6#include "audio_core/renderer/command/mix/mix_ramp_grouped.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { 10void MixRampGroupedCommand::Dump(const AudioRenderer::CommandListProcessor& processor,
11 std::string& string) {
11 string += "MixRampGroupedCommand"; 12 string += "MixRampGroupedCommand";
12 for (u32 i = 0; i < buffer_count; i++) { 13 for (u32 i = 0; i < buffer_count; i++) {
13 string += fmt::format("\n\t{}", i); 14 string += fmt::format("\n\t{}", i);
@@ -21,7 +22,7 @@ void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, st
21 } 22 }
22} 23}
23 24
24void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor) { 25void MixRampGroupedCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
25 std::span<s32> prev_samples = {reinterpret_cast<s32*>(previous_samples), MaxMixBuffers}; 26 std::span<s32> prev_samples = {reinterpret_cast<s32*>(previous_samples), MaxMixBuffers};
26 27
27 for (u32 i = 0; i < buffer_count; i++) { 28 for (u32 i = 0; i < buffer_count; i++) {
@@ -58,8 +59,8 @@ void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor)
58 } 59 }
59} 60}
60 61
61bool MixRampGroupedCommand::Verify(const ADSP::CommandListProcessor& processor) { 62bool MixRampGroupedCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
62 return true; 63 return true;
63} 64}
64 65
65} // namespace AudioCore::AudioRenderer 66} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h
index 3b0ce67ef..9621e42a3 100644
--- a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h
+++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h
@@ -9,11 +9,12 @@
9#include "audio_core/renderer/command/icommand.h" 9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::ADSP::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor; 13class CommandListProcessor;
15} 14}
16 15
16namespace AudioCore::Renderer {
17
17/** 18/**
18 * AudioRenderer command for mixing multiple input mix buffers to multiple output mix buffers, with 19 * AudioRenderer command for mixing multiple input mix buffers to multiple output mix buffers, with
19 * a volume applied to the input, and volume ramping to smooth out the transition. 20 * a volume applied to the input, and volume ramping to smooth out the transition.
@@ -25,14 +26,14 @@ struct MixRampGroupedCommand : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,7 +41,7 @@ struct MixRampGroupedCommand : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// Fixed point precision 46 /// Fixed point precision
46 u8 precision; 47 u8 precision;
@@ -58,4 +59,4 @@ struct MixRampGroupedCommand : ICommand {
58 CpuAddr previous_samples; 59 CpuAddr previous_samples;
59}; 60};
60 61
61} // namespace AudioCore::AudioRenderer 62} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/volume.cpp b/src/audio_core/renderer/command/mix/volume.cpp
index b045fb062..92baf6cc3 100644
--- a/src/audio_core/renderer/command/mix/volume.cpp
+++ b/src/audio_core/renderer/command/mix/volume.cpp
@@ -1,12 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/mix/volume.h" 5#include "audio_core/renderer/command/mix/volume.h"
6#include "common/fixed_point.h" 6#include "common/fixed_point.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10/** 10/**
11 * Apply volume to the input mix buffer, saving to the output buffer. 11 * Apply volume to the input mix buffer, saving to the output buffer.
12 * 12 *
@@ -29,7 +29,7 @@ static void ApplyUniformGain(std::span<s32> output, std::span<const s32> input,
29 } 29 }
30} 30}
31 31
32void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 32void VolumeCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
33 std::string& string) { 33 std::string& string) {
34 string += fmt::format("VolumeCommand"); 34 string += fmt::format("VolumeCommand");
35 string += fmt::format("\n\tinput {:02X}", input_index); 35 string += fmt::format("\n\tinput {:02X}", input_index);
@@ -38,7 +38,7 @@ void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& proc
38 string += "\n"; 38 string += "\n";
39} 39}
40 40
41void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) { 41void VolumeCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
42 // If input and output buffers are the same, and the volume is 1.0f, this won't do 42 // If input and output buffers are the same, and the volume is 1.0f, this won't do
43 // anything, so just skip. 43 // anything, so just skip.
44 if (input_index == output_index && volume == 1.0f) { 44 if (input_index == output_index && volume == 1.0f) {
@@ -65,8 +65,8 @@ void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) {
65 } 65 }
66} 66}
67 67
68bool VolumeCommand::Verify(const ADSP::CommandListProcessor& processor) { 68bool VolumeCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
69 return true; 69 return true;
70} 70}
71 71
72} // namespace AudioCore::AudioRenderer 72} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/volume.h b/src/audio_core/renderer/command/mix/volume.h
index 6ae9fb794..fbb8156ca 100644
--- a/src/audio_core/renderer/command/mix/volume.h
+++ b/src/audio_core/renderer/command/mix/volume.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for applying volume to a mix buffer. 18 * AudioRenderer command for applying volume to a mix buffer.
18 */ 19 */
@@ -23,14 +24,14 @@ struct VolumeCommand : ICommand {
23 * @param processor - The CommandListProcessor processing this command. 24 * @param processor - The CommandListProcessor processing this command.
24 * @param string - The string to print into. 25 * @param string - The string to print into.
25 */ 26 */
26 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 27 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
27 28
28 /** 29 /**
29 * Process this command. 30 * Process this command.
30 * 31 *
31 * @param processor - The CommandListProcessor processing this command. 32 * @param processor - The CommandListProcessor processing this command.
32 */ 33 */
33 void Process(const ADSP::CommandListProcessor& processor) override; 34 void Process(const AudioRenderer::CommandListProcessor& processor) override;
34 35
35 /** 36 /**
36 * Verify this command's data is valid. 37 * Verify this command's data is valid.
@@ -38,7 +39,7 @@ struct VolumeCommand : ICommand {
38 * @param processor - The CommandListProcessor processing this command. 39 * @param processor - The CommandListProcessor processing this command.
39 * @return True if the command is valid, otherwise false. 40 * @return True if the command is valid, otherwise false.
40 */ 41 */
41 bool Verify(const ADSP::CommandListProcessor& processor) override; 42 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
42 43
43 /// Fixed point precision 44 /// Fixed point precision
44 u8 precision; 45 u8 precision;
@@ -50,4 +51,4 @@ struct VolumeCommand : ICommand {
50 f32 volume; 51 f32 volume;
51}; 52};
52 53
53} // namespace AudioCore::AudioRenderer 54} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/volume_ramp.cpp b/src/audio_core/renderer/command/mix/volume_ramp.cpp
index 424307148..fdc751957 100644
--- a/src/audio_core/renderer/command/mix/volume_ramp.cpp
+++ b/src/audio_core/renderer/command/mix/volume_ramp.cpp
@@ -1,11 +1,11 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/mix/volume_ramp.h" 5#include "audio_core/renderer/command/mix/volume_ramp.h"
6#include "common/fixed_point.h" 6#include "common/fixed_point.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9/** 9/**
10 * Apply volume with ramping to the input mix buffer, saving to the output buffer. 10 * Apply volume with ramping to the input mix buffer, saving to the output buffer.
11 * 11 *
@@ -38,7 +38,8 @@ static void ApplyLinearEnvelopeGain(std::span<s32> output, std::span<const s32>
38 } 38 }
39} 39}
40 40
41void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) { 41void VolumeRampCommand::Dump(const AudioRenderer::CommandListProcessor& processor,
42 std::string& string) {
42 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)}; 43 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
43 string += fmt::format("VolumeRampCommand"); 44 string += fmt::format("VolumeRampCommand");
44 string += fmt::format("\n\tinput {:02X}", input_index); 45 string += fmt::format("\n\tinput {:02X}", input_index);
@@ -49,7 +50,7 @@ void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::s
49 string += "\n"; 50 string += "\n";
50} 51}
51 52
52void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) { 53void VolumeRampCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
53 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count, 54 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
54 processor.sample_count)}; 55 processor.sample_count)};
55 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count, 56 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
@@ -77,8 +78,8 @@ void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) {
77 } 78 }
78} 79}
79 80
80bool VolumeRampCommand::Verify(const ADSP::CommandListProcessor& processor) { 81bool VolumeRampCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
81 return true; 82 return true;
82} 83}
83 84
84} // namespace AudioCore::AudioRenderer 85} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/mix/volume_ramp.h b/src/audio_core/renderer/command/mix/volume_ramp.h
index 77b61547e..d9794fb95 100644
--- a/src/audio_core/renderer/command/mix/volume_ramp.h
+++ b/src/audio_core/renderer/command/mix/volume_ramp.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for applying volume to a mix buffer, with ramping for the volume to smooth 18 * AudioRenderer command for applying volume to a mix buffer, with ramping for the volume to smooth
18 * out the transition. 19 * out the transition.
@@ -24,14 +25,14 @@ struct VolumeRampCommand : ICommand {
24 * @param processor - The CommandListProcessor processing this command. 25 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into. 26 * @param string - The string to print into.
26 */ 27 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 28 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
28 29
29 /** 30 /**
30 * Process this command. 31 * Process this command.
31 * 32 *
32 * @param processor - The CommandListProcessor processing this command. 33 * @param processor - The CommandListProcessor processing this command.
33 */ 34 */
34 void Process(const ADSP::CommandListProcessor& processor) override; 35 void Process(const AudioRenderer::CommandListProcessor& processor) override;
35 36
36 /** 37 /**
37 * Verify this command's data is valid. 38 * Verify this command's data is valid.
@@ -39,7 +40,7 @@ struct VolumeRampCommand : ICommand {
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false. 41 * @return True if the command is valid, otherwise false.
41 */ 42 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override; 43 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
43 44
44 /// Fixed point precision 45 /// Fixed point precision
45 u8 precision; 46 u8 precision;
@@ -53,4 +54,4 @@ struct VolumeRampCommand : ICommand {
53 f32 volume; 54 f32 volume;
54}; 55};
55 56
56} // namespace AudioCore::AudioRenderer 57} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/performance/performance.cpp b/src/audio_core/renderer/command/performance/performance.cpp
index 4a881547f..f0cfcc9fd 100644
--- a/src/audio_core/renderer/command/performance/performance.cpp
+++ b/src/audio_core/renderer/command/performance/performance.cpp
@@ -1,25 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/performance/performance.h" 5#include "audio_core/renderer/command/performance/performance.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/core_timing.h" 7#include "core/core_timing.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11void PerformanceCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 11void PerformanceCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
12 std::string& string) { 12 std::string& string) {
13 string += fmt::format("PerformanceCommand\n\tstate {}\n", static_cast<u32>(state)); 13 string += fmt::format("PerformanceCommand\n\tstate {}\n", static_cast<u32>(state));
14} 14}
15 15
16void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) { 16void PerformanceCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
17 auto base{entry_address.translated_address}; 17 auto base{entry_address.translated_address};
18 if (state == PerformanceState::Start) { 18 if (state == PerformanceState::Start) {
19 auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)}; 19 auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)};
20 *start_time_ptr = 20 *start_time_ptr =
21 static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - 21 static_cast<u32>(processor.system->CoreTiming().GetGlobalTimeUs().count() -
22 processor.current_processing_time); 22 processor.start_time - processor.current_processing_time);
23 } else if (state == PerformanceState::Stop) { 23 } else if (state == PerformanceState::Stop) {
24 auto processed_time_ptr{ 24 auto processed_time_ptr{
25 reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)}; 25 reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)};
@@ -27,14 +27,14 @@ void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) {
27 reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)}; 27 reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)};
28 28
29 *processed_time_ptr = 29 *processed_time_ptr =
30 static_cast<u32>(processor.system->CoreTiming().GetClockTicks() - processor.start_time - 30 static_cast<u32>(processor.system->CoreTiming().GetGlobalTimeUs().count() -
31 processor.current_processing_time); 31 processor.start_time - processor.current_processing_time);
32 (*entry_count_ptr)++; 32 (*entry_count_ptr)++;
33 } 33 }
34} 34}
35 35
36bool PerformanceCommand::Verify(const ADSP::CommandListProcessor& processor) { 36bool PerformanceCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
37 return true; 37 return true;
38} 38}
39 39
40} // namespace AudioCore::AudioRenderer 40} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/performance/performance.h b/src/audio_core/renderer/command/performance/performance.h
index 11a7d6c08..522e51e34 100644
--- a/src/audio_core/renderer/command/performance/performance.h
+++ b/src/audio_core/renderer/command/performance/performance.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/performance/performance_manager.h" 10#include "audio_core/renderer/performance/performance_manager.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for writing AudioRenderer performance metrics back to the sysmodule. 20 * AudioRenderer command for writing AudioRenderer performance metrics back to the sysmodule.
20 */ 21 */
@@ -25,14 +26,14 @@ struct PerformanceCommand : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,7 +41,7 @@ struct PerformanceCommand : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// State of the performance 46 /// State of the performance
46 PerformanceState state; 47 PerformanceState state;
@@ -48,4 +49,4 @@ struct PerformanceCommand : ICommand {
48 PerformanceEntryAddresses entry_address; 49 PerformanceEntryAddresses entry_address;
49}; 50};
50 51
51} // namespace AudioCore::AudioRenderer 52} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp
index 1fd90308a..f9b289887 100644
--- a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp
+++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp
@@ -1,13 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 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 "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
5#include "audio_core/renderer/command/resample/downmix_6ch_to_2ch.h" 5#include "audio_core/renderer/command/resample/downmix_6ch_to_2ch.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8 8
9void DownMix6chTo2chCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 9void DownMix6chTo2chCommand::Dump(
10 std::string& string) { 10 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
11 string += fmt::format("DownMix6chTo2chCommand\n\tinputs: "); 11 string += fmt::format("DownMix6chTo2chCommand\n\tinputs: ");
12 for (u32 i = 0; i < MaxChannels; i++) { 12 for (u32 i = 0; i < MaxChannels; i++) {
13 string += fmt::format("{:02X}, ", inputs[i]); 13 string += fmt::format("{:02X}, ", inputs[i]);
@@ -19,7 +19,7 @@ void DownMix6chTo2chCommand::Dump([[maybe_unused]] const ADSP::CommandListProces
19 string += "\n"; 19 string += "\n";
20} 20}
21 21
22void DownMix6chTo2chCommand::Process(const ADSP::CommandListProcessor& processor) { 22void DownMix6chTo2chCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
23 auto in_front_left{ 23 auto in_front_left{
24 processor.mix_buffers.subspan(inputs[0] * processor.sample_count, processor.sample_count)}; 24 processor.mix_buffers.subspan(inputs[0] * processor.sample_count, processor.sample_count)};
25 auto in_front_right{ 25 auto in_front_right{
@@ -67,8 +67,8 @@ void DownMix6chTo2chCommand::Process(const ADSP::CommandListProcessor& processor
67 std::memset(out_back_right.data(), 0, out_back_right.size_bytes()); 67 std::memset(out_back_right.data(), 0, out_back_right.size_bytes());
68} 68}
69 69
70bool DownMix6chTo2chCommand::Verify(const ADSP::CommandListProcessor& processor) { 70bool DownMix6chTo2chCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
71 return true; 71 return true;
72} 72}
73 73
74} // namespace AudioCore::AudioRenderer 74} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h
index dc133a73b..96cbc5506 100644
--- a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h
+++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h
@@ -9,11 +9,12 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/fixed_point.h" 10#include "common/fixed_point.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::ADSP::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor; 13class CommandListProcessor;
15} 14}
16 15
16namespace AudioCore::Renderer {
17
17/** 18/**
18 * AudioRenderer command for downmixing 6 channels to 2. 19 * AudioRenderer command for downmixing 6 channels to 2.
19 * Channel layout (SMPTE): 20 * Channel layout (SMPTE):
@@ -31,14 +32,14 @@ struct DownMix6chTo2chCommand : ICommand {
31 * @param processor - The CommandListProcessor processing this command. 32 * @param processor - The CommandListProcessor processing this command.
32 * @param string - The string to print into. 33 * @param string - The string to print into.
33 */ 34 */
34 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 35 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
35 36
36 /** 37 /**
37 * Process this command. 38 * Process this command.
38 * 39 *
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 */ 41 */
41 void Process(const ADSP::CommandListProcessor& processor) override; 42 void Process(const AudioRenderer::CommandListProcessor& processor) override;
42 43
43 /** 44 /**
44 * Verify this command's data is valid. 45 * Verify this command's data is valid.
@@ -46,7 +47,7 @@ struct DownMix6chTo2chCommand : ICommand {
46 * @param processor - The CommandListProcessor processing this command. 47 * @param processor - The CommandListProcessor processing this command.
47 * @return True if the command is valid, otherwise false. 48 * @return True if the command is valid, otherwise false.
48 */ 49 */
49 bool Verify(const ADSP::CommandListProcessor& processor) override; 50 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
50 51
51 /// Input mix buffer offsets for each channel 52 /// Input mix buffer offsets for each channel
52 std::array<s16, MaxChannels> inputs; 53 std::array<s16, MaxChannels> inputs;
@@ -56,4 +57,4 @@ struct DownMix6chTo2chCommand : ICommand {
56 std::array<Common::FixedPoint<48, 16>, 4> down_mix_coeff; 57 std::array<Common::FixedPoint<48, 16>, 4> down_mix_coeff;
57}; 58};
58 59
59} // namespace AudioCore::AudioRenderer 60} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/resample/resample.cpp b/src/audio_core/renderer/command/resample/resample.cpp
index 070c9d2b8..51f4ba39e 100644
--- a/src/audio_core/renderer/command/resample/resample.cpp
+++ b/src/audio_core/renderer/command/resample/resample.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/command/resample/resample.h" 4#include "audio_core/renderer/command/resample/resample.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input, 8static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input,
9 const Common::FixedPoint<49, 15>& sample_rate_ratio, 9 const Common::FixedPoint<49, 15>& sample_rate_ratio,
@@ -880,4 +880,4 @@ void Resample(std::span<s32> output, std::span<const s16> input,
880 } 880 }
881} 881}
882 882
883} // namespace AudioCore::AudioRenderer 883} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/resample/resample.h b/src/audio_core/renderer/command/resample/resample.h
index ba9209b82..134aff0c9 100644
--- a/src/audio_core/renderer/command/resample/resample.h
+++ b/src/audio_core/renderer/command/resample/resample.h
@@ -9,7 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/fixed_point.h" 10#include "common/fixed_point.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13/** 13/**
14 * Resample an input buffer into an output buffer, according to the sample_rate_ratio. 14 * Resample an input buffer into an output buffer, according to the sample_rate_ratio.
15 * 15 *
@@ -26,4 +26,4 @@ void Resample(std::span<s32> output, std::span<const s16> input,
26 const Common::FixedPoint<49, 15>& sample_rate_ratio, 26 const Common::FixedPoint<49, 15>& sample_rate_ratio,
27 Common::FixedPoint<49, 15>& fraction, u32 samples_to_write, SrcQuality src_quality); 27 Common::FixedPoint<49, 15>& fraction, u32 samples_to_write, SrcQuality src_quality);
28 28
29} // namespace AudioCore::AudioRenderer 29} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/resample/upsample.cpp b/src/audio_core/renderer/command/resample/upsample.cpp
index 86ddee1a4..691d70390 100644
--- a/src/audio_core/renderer/command/resample/upsample.cpp
+++ b/src/audio_core/renderer/command/resample/upsample.cpp
@@ -3,11 +3,11 @@
3 3
4#include <array> 4#include <array>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/resample/upsample.h" 7#include "audio_core/renderer/command/resample/upsample.h"
8#include "audio_core/renderer/upsampler/upsampler_info.h" 8#include "audio_core/renderer/upsampler/upsampler_info.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11/** 11/**
12 * Upsampling impl. Input must be 8K, 16K or 32K, output is 48K. 12 * Upsampling impl. Input must be 8K, 16K or 32K, output is 48K.
13 * 13 *
@@ -198,7 +198,7 @@ static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
198 } 198 }
199} 199}
200 200
201auto UpsampleCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 201auto UpsampleCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
202 std::string& string) -> void { 202 std::string& string) -> void {
203 string += fmt::format("UpsampleCommand\n\tsource_sample_count {} source_sample_rate {}", 203 string += fmt::format("UpsampleCommand\n\tsource_sample_count {} source_sample_rate {}",
204 source_sample_count, source_sample_rate); 204 source_sample_count, source_sample_rate);
@@ -213,7 +213,7 @@ auto UpsampleCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& pr
213 string += "\n"; 213 string += "\n";
214} 214}
215 215
216void UpsampleCommand::Process(const ADSP::CommandListProcessor& processor) { 216void UpsampleCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
217 const auto info{reinterpret_cast<UpsamplerInfo*>(upsampler_info)}; 217 const auto info{reinterpret_cast<UpsamplerInfo*>(upsampler_info)};
218 const auto input_count{std::min(info->input_count, buffer_count)}; 218 const auto input_count{std::min(info->input_count, buffer_count)};
219 const std::span<const s16> inputs_{reinterpret_cast<const s16*>(inputs), input_count}; 219 const std::span<const s16> inputs_{reinterpret_cast<const s16*>(inputs), input_count};
@@ -234,8 +234,8 @@ void UpsampleCommand::Process(const ADSP::CommandListProcessor& processor) {
234 } 234 }
235} 235}
236 236
237bool UpsampleCommand::Verify(const ADSP::CommandListProcessor& processor) { 237bool UpsampleCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
238 return true; 238 return true;
239} 239}
240 240
241} // namespace AudioCore::AudioRenderer 241} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/resample/upsample.h b/src/audio_core/renderer/command/resample/upsample.h
index bfc94e8af..877271ba9 100644
--- a/src/audio_core/renderer/command/resample/upsample.h
+++ b/src/audio_core/renderer/command/resample/upsample.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for upsampling a mix buffer to 48Khz. 18 * AudioRenderer command for upsampling a mix buffer to 48Khz.
18 * Input must be 8Khz, 16Khz or 32Khz, and output will be 48Khz. 19 * Input must be 8Khz, 16Khz or 32Khz, and output will be 48Khz.
@@ -24,14 +25,14 @@ struct UpsampleCommand : ICommand {
24 * @param processor - The CommandListProcessor processing this command. 25 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into. 26 * @param string - The string to print into.
26 */ 27 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 28 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
28 29
29 /** 30 /**
30 * Process this command. 31 * Process this command.
31 * 32 *
32 * @param processor - The CommandListProcessor processing this command. 33 * @param processor - The CommandListProcessor processing this command.
33 */ 34 */
34 void Process(const ADSP::CommandListProcessor& processor) override; 35 void Process(const AudioRenderer::CommandListProcessor& processor) override;
35 36
36 /** 37 /**
37 * Verify this command's data is valid. 38 * Verify this command's data is valid.
@@ -39,7 +40,7 @@ struct UpsampleCommand : ICommand {
39 * @param processor - The CommandListProcessor processing this command. 40 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false. 41 * @return True if the command is valid, otherwise false.
41 */ 42 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override; 43 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
43 44
44 /// Pointer to the output samples buffer. 45 /// Pointer to the output samples buffer.
45 CpuAddr samples_buffer; 46 CpuAddr samples_buffer;
@@ -57,4 +58,4 @@ struct UpsampleCommand : ICommand {
57 CpuAddr upsampler_info; 58 CpuAddr upsampler_info;
58}; 59};
59 60
60} // namespace AudioCore::AudioRenderer 61} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp
index e2ce59792..e056d15a6 100644
--- a/src/audio_core/renderer/command/sink/circular_buffer.cpp
+++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp
@@ -3,14 +3,14 @@
3 3
4#include <vector> 4#include <vector>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/sink/circular_buffer.h" 7#include "audio_core/renderer/command/sink/circular_buffer.h"
8#include "core/memory.h" 8#include "core/memory.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12void CircularBufferSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 12void CircularBufferSinkCommand::Dump(
13 std::string& string) { 13 [[maybe_unused]] const AudioRenderer::CommandListProcessor& processor, std::string& string) {
14 string += fmt::format( 14 string += fmt::format(
15 "CircularBufferSinkCommand\n\tinput_count {} ring size {:04X} ring pos {:04X}\n\tinputs: ", 15 "CircularBufferSinkCommand\n\tinput_count {} ring size {:04X} ring pos {:04X}\n\tinputs: ",
16 input_count, size, pos); 16 input_count, size, pos);
@@ -20,7 +20,7 @@ void CircularBufferSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListPro
20 string += "\n"; 20 string += "\n";
21} 21}
22 22
23void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& processor) { 23void CircularBufferSinkCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
24 constexpr s32 min{std::numeric_limits<s16>::min()}; 24 constexpr s32 min{std::numeric_limits<s16>::min()};
25 constexpr s32 max{std::numeric_limits<s16>::max()}; 25 constexpr s32 max{std::numeric_limits<s16>::max()};
26 26
@@ -41,8 +41,8 @@ void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& proces
41 } 41 }
42} 42}
43 43
44bool CircularBufferSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { 44bool CircularBufferSinkCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
45 return true; 45 return true;
46} 46}
47 47
48} // namespace AudioCore::AudioRenderer 48} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.h b/src/audio_core/renderer/command/sink/circular_buffer.h
index e7d5be26e..a3234a406 100644
--- a/src/audio_core/renderer/command/sink/circular_buffer.h
+++ b/src/audio_core/renderer/command/sink/circular_buffer.h
@@ -8,11 +8,12 @@
8#include "audio_core/renderer/command/icommand.h" 8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::ADSP::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor; 12class CommandListProcessor;
14} 13}
15 14
15namespace AudioCore::Renderer {
16
16/** 17/**
17 * AudioRenderer command for sinking samples to a circular buffer. 18 * AudioRenderer command for sinking samples to a circular buffer.
18 */ 19 */
@@ -23,14 +24,14 @@ struct CircularBufferSinkCommand : ICommand {
23 * @param processor - The CommandListProcessor processing this command. 24 * @param processor - The CommandListProcessor processing this command.
24 * @param string - The string to print into. 25 * @param string - The string to print into.
25 */ 26 */
26 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 27 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
27 28
28 /** 29 /**
29 * Process this command. 30 * Process this command.
30 * 31 *
31 * @param processor - The CommandListProcessor processing this command. 32 * @param processor - The CommandListProcessor processing this command.
32 */ 33 */
33 void Process(const ADSP::CommandListProcessor& processor) override; 34 void Process(const AudioRenderer::CommandListProcessor& processor) override;
34 35
35 /** 36 /**
36 * Verify this command's data is valid. 37 * Verify this command's data is valid.
@@ -38,7 +39,7 @@ struct CircularBufferSinkCommand : ICommand {
38 * @param processor - The CommandListProcessor processing this command. 39 * @param processor - The CommandListProcessor processing this command.
39 * @return True if the command is valid, otherwise false. 40 * @return True if the command is valid, otherwise false.
40 */ 41 */
41 bool Verify(const ADSP::CommandListProcessor& processor) override; 42 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
42 43
43 /// Number of input mix buffers 44 /// Number of input mix buffers
44 u32 input_count; 45 u32 input_count;
@@ -52,4 +53,4 @@ struct CircularBufferSinkCommand : ICommand {
52 u32 pos; 53 u32 pos;
53}; 54};
54 55
55} // namespace AudioCore::AudioRenderer 56} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp
index 5f74dd7ad..3480ed475 100644
--- a/src/audio_core/renderer/command/sink/device.cpp
+++ b/src/audio_core/renderer/command/sink/device.cpp
@@ -3,13 +3,13 @@
3 3
4#include <algorithm> 4#include <algorithm>
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/adsp/apps/audio_renderer/command_list_processor.h"
7#include "audio_core/renderer/command/sink/device.h" 7#include "audio_core/renderer/command/sink/device.h"
8#include "audio_core/sink/sink.h" 8#include "audio_core/sink/sink.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12void DeviceSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, 12void DeviceSinkCommand::Dump([[maybe_unused]] const AudioRenderer::CommandListProcessor& processor,
13 std::string& string) { 13 std::string& string) {
14 string += fmt::format("DeviceSinkCommand\n\t{} session {} input_count {}\n\tinputs: ", 14 string += fmt::format("DeviceSinkCommand\n\t{} session {} input_count {}\n\tinputs: ",
15 std::string_view(name), session_id, input_count); 15 std::string_view(name), session_id, input_count);
@@ -19,7 +19,7 @@ void DeviceSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor&
19 string += "\n"; 19 string += "\n";
20} 20}
21 21
22void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) { 22void DeviceSinkCommand::Process(const AudioRenderer::CommandListProcessor& processor) {
23 constexpr s32 min = std::numeric_limits<s16>::min(); 23 constexpr s32 min = std::numeric_limits<s16>::min();
24 constexpr s32 max = std::numeric_limits<s16>::max(); 24 constexpr s32 max = std::numeric_limits<s16>::max();
25 25
@@ -51,8 +51,8 @@ void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
51 } 51 }
52} 52}
53 53
54bool DeviceSinkCommand::Verify(const ADSP::CommandListProcessor& processor) { 54bool DeviceSinkCommand::Verify(const AudioRenderer::CommandListProcessor& processor) {
55 return true; 55 return true;
56} 56}
57 57
58} // namespace AudioCore::AudioRenderer 58} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/command/sink/device.h b/src/audio_core/renderer/command/sink/device.h
index 1099bcf8c..385b51ecc 100644
--- a/src/audio_core/renderer/command/sink/device.h
+++ b/src/audio_core/renderer/command/sink/device.h
@@ -10,11 +10,12 @@
10#include "audio_core/renderer/command/icommand.h" 10#include "audio_core/renderer/command/icommand.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::ADSP::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor; 14class CommandListProcessor;
16} 15}
17 16
17namespace AudioCore::Renderer {
18
18/** 19/**
19 * AudioRenderer command for sinking samples to an output device. 20 * AudioRenderer command for sinking samples to an output device.
20 */ 21 */
@@ -25,14 +26,14 @@ struct DeviceSinkCommand : ICommand {
25 * @param processor - The CommandListProcessor processing this command. 26 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into. 27 * @param string - The string to print into.
27 */ 28 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; 29 void Dump(const AudioRenderer::CommandListProcessor& processor, std::string& string) override;
29 30
30 /** 31 /**
31 * Process this command. 32 * Process this command.
32 * 33 *
33 * @param processor - The CommandListProcessor processing this command. 34 * @param processor - The CommandListProcessor processing this command.
34 */ 35 */
35 void Process(const ADSP::CommandListProcessor& processor) override; 36 void Process(const AudioRenderer::CommandListProcessor& processor) override;
36 37
37 /** 38 /**
38 * Verify this command's data is valid. 39 * Verify this command's data is valid.
@@ -40,7 +41,7 @@ struct DeviceSinkCommand : ICommand {
40 * @param processor - The CommandListProcessor processing this command. 41 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false. 42 * @return True if the command is valid, otherwise false.
42 */ 43 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override; 44 bool Verify(const AudioRenderer::CommandListProcessor& processor) override;
44 45
45 /// Device name 46 /// Device name
46 char name[0x100]; 47 char name[0x100];
@@ -54,4 +55,4 @@ struct DeviceSinkCommand : ICommand {
54 std::array<s16, MaxChannels> inputs; 55 std::array<s16, MaxChannels> inputs;
55}; 56};
56 57
57} // namespace AudioCore::AudioRenderer 58} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/aux_.cpp b/src/audio_core/renderer/effect/aux_.cpp
index 51e780ef1..1c1411eff 100644
--- a/src/audio_core/renderer/effect/aux_.cpp
+++ b/src/audio_core/renderer/effect/aux_.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/aux_.h" 4#include "audio_core/renderer/effect/aux_.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, 8void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
9 const PoolMapper& pool_mapper) { 9 const PoolMapper& pool_mapper) {
@@ -90,4 +90,4 @@ CpuAddr AuxInfo::GetWorkbuffer(s32 index) {
90 return workbuffers[index].GetReference(true); 90 return workbuffers[index].GetReference(true);
91} 91}
92 92
93} // namespace AudioCore::AudioRenderer 93} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/aux_.h b/src/audio_core/renderer/effect/aux_.h
index 4d3d9e3d9..c5b3058da 100644
--- a/src/audio_core/renderer/effect/aux_.h
+++ b/src/audio_core/renderer/effect/aux_.h
@@ -9,7 +9,7 @@
9#include "audio_core/renderer/effect/effect_info_base.h" 9#include "audio_core/renderer/effect/effect_info_base.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13/** 13/**
14 * Auxiliary Buffer used for Aux commands. 14 * Auxiliary Buffer used for Aux commands.
15 * Send and return buffers are available (names from the game's perspective). 15 * Send and return buffers are available (names from the game's perspective).
@@ -120,4 +120,4 @@ public:
120 CpuAddr GetWorkbuffer(s32 index) override; 120 CpuAddr GetWorkbuffer(s32 index) override;
121}; 121};
122 122
123} // namespace AudioCore::AudioRenderer 123} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/biquad_filter.cpp b/src/audio_core/renderer/effect/biquad_filter.cpp
index a1efb3231..08161d840 100644
--- a/src/audio_core/renderer/effect/biquad_filter.cpp
+++ b/src/audio_core/renderer/effect/biquad_filter.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/biquad_filter.h" 4#include "audio_core/renderer/effect/biquad_filter.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, 8void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { 9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
@@ -49,4 +49,4 @@ void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {}
49void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, 49void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state,
50 EffectResultState& dsp_state) {} 50 EffectResultState& dsp_state) {}
51 51
52} // namespace AudioCore::AudioRenderer 52} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/biquad_filter.h b/src/audio_core/renderer/effect/biquad_filter.h
index f53fd5bab..5a22899ab 100644
--- a/src/audio_core/renderer/effect/biquad_filter.h
+++ b/src/audio_core/renderer/effect/biquad_filter.h
@@ -9,7 +9,7 @@
9#include "audio_core/renderer/effect/effect_info_base.h" 9#include "audio_core/renderer/effect/effect_info_base.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13 13
14class BiquadFilterInfo : public EffectInfoBase { 14class BiquadFilterInfo : public EffectInfoBase {
15public: 15public:
@@ -76,4 +76,4 @@ public:
76 void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; 76 void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
77}; 77};
78 78
79} // namespace AudioCore::AudioRenderer 79} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/buffer_mixer.cpp b/src/audio_core/renderer/effect/buffer_mixer.cpp
index 9c8877f01..826e246ec 100644
--- a/src/audio_core/renderer/effect/buffer_mixer.cpp
+++ b/src/audio_core/renderer/effect/buffer_mixer.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/buffer_mixer.h" 4#include "audio_core/renderer/effect/buffer_mixer.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, 8void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info,
9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { 9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
@@ -46,4 +46,4 @@ void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {}
46void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state, 46void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state,
47 EffectResultState& dsp_state) {} 47 EffectResultState& dsp_state) {}
48 48
49} // namespace AudioCore::AudioRenderer 49} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/buffer_mixer.h b/src/audio_core/renderer/effect/buffer_mixer.h
index 23eed4a8b..0c01ef38d 100644
--- a/src/audio_core/renderer/effect/buffer_mixer.h
+++ b/src/audio_core/renderer/effect/buffer_mixer.h
@@ -9,7 +9,7 @@
9#include "audio_core/renderer/effect/effect_info_base.h" 9#include "audio_core/renderer/effect/effect_info_base.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13 13
14class BufferMixerInfo : public EffectInfoBase { 14class BufferMixerInfo : public EffectInfoBase {
15public: 15public:
@@ -72,4 +72,4 @@ public:
72 void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; 72 void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
73}; 73};
74 74
75} // namespace AudioCore::AudioRenderer 75} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/capture.cpp b/src/audio_core/renderer/effect/capture.cpp
index 3f038efdb..dfa062a59 100644
--- a/src/audio_core/renderer/effect/capture.cpp
+++ b/src/audio_core/renderer/effect/capture.cpp
@@ -4,7 +4,7 @@
4#include "audio_core/renderer/effect/aux_.h" 4#include "audio_core/renderer/effect/aux_.h"
5#include "audio_core/renderer/effect/capture.h" 5#include "audio_core/renderer/effect/capture.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8 8
9void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, 9void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
10 const PoolMapper& pool_mapper) { 10 const PoolMapper& pool_mapper) {
@@ -79,4 +79,4 @@ CpuAddr CaptureInfo::GetWorkbuffer(s32 index) {
79 return workbuffers[index].GetReference(true); 79 return workbuffers[index].GetReference(true);
80} 80}
81 81
82} // namespace AudioCore::AudioRenderer 82} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/capture.h b/src/audio_core/renderer/effect/capture.h
index 6fbed8e6b..cbe71e22a 100644
--- a/src/audio_core/renderer/effect/capture.h
+++ b/src/audio_core/renderer/effect/capture.h
@@ -9,7 +9,7 @@
9#include "audio_core/renderer/effect/effect_info_base.h" 9#include "audio_core/renderer/effect/effect_info_base.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13 13
14class CaptureInfo : public EffectInfoBase { 14class CaptureInfo : public EffectInfoBase {
15public: 15public:
@@ -62,4 +62,4 @@ public:
62 CpuAddr GetWorkbuffer(s32 index) override; 62 CpuAddr GetWorkbuffer(s32 index) override;
63}; 63};
64 64
65} // namespace AudioCore::AudioRenderer 65} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/compressor.cpp b/src/audio_core/renderer/effect/compressor.cpp
index 220ae02f9..fea0aefcf 100644
--- a/src/audio_core/renderer/effect/compressor.cpp
+++ b/src/audio_core/renderer/effect/compressor.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/compressor.h" 4#include "audio_core/renderer/effect/compressor.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, 8void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info,
9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {} 9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {}
@@ -37,4 +37,4 @@ CpuAddr CompressorInfo::GetWorkbuffer(s32 index) {
37 return GetSingleBuffer(index); 37 return GetSingleBuffer(index);
38} 38}
39 39
40} // namespace AudioCore::AudioRenderer 40} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/compressor.h b/src/audio_core/renderer/effect/compressor.h
index 019a5ae58..cda55c284 100644
--- a/src/audio_core/renderer/effect/compressor.h
+++ b/src/audio_core/renderer/effect/compressor.h
@@ -10,7 +10,7 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/fixed_point.h" 11#include "common/fixed_point.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14 14
15class CompressorInfo : public EffectInfoBase { 15class CompressorInfo : public EffectInfoBase {
16public: 16public:
@@ -103,4 +103,4 @@ public:
103 CpuAddr GetWorkbuffer(s32 index) override; 103 CpuAddr GetWorkbuffer(s32 index) override;
104}; 104};
105 105
106} // namespace AudioCore::AudioRenderer 106} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/delay.cpp b/src/audio_core/renderer/effect/delay.cpp
index d9853efd9..e038d4498 100644
--- a/src/audio_core/renderer/effect/delay.cpp
+++ b/src/audio_core/renderer/effect/delay.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/delay.h" 4#include "audio_core/renderer/effect/delay.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, 8void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
9 const PoolMapper& pool_mapper) { 9 const PoolMapper& pool_mapper) {
@@ -90,4 +90,4 @@ CpuAddr DelayInfo::GetWorkbuffer(s32 index) {
90 return GetSingleBuffer(index); 90 return GetSingleBuffer(index);
91} 91}
92 92
93} // namespace AudioCore::AudioRenderer 93} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/delay.h b/src/audio_core/renderer/effect/delay.h
index accc42a06..47417fbc6 100644
--- a/src/audio_core/renderer/effect/delay.h
+++ b/src/audio_core/renderer/effect/delay.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/fixed_point.h" 12#include "common/fixed_point.h"
13 13
14namespace AudioCore::AudioRenderer { 14namespace AudioCore::Renderer {
15 15
16class DelayInfo : public EffectInfoBase { 16class DelayInfo : public EffectInfoBase {
17public: 17public:
@@ -132,4 +132,4 @@ public:
132 CpuAddr GetWorkbuffer(s32 index) override; 132 CpuAddr GetWorkbuffer(s32 index) override;
133}; 133};
134 134
135} // namespace AudioCore::AudioRenderer 135} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/effect_context.cpp b/src/audio_core/renderer/effect/effect_context.cpp
index 74c7801c9..00f6d7822 100644
--- a/src/audio_core/renderer/effect/effect_context.cpp
+++ b/src/audio_core/renderer/effect/effect_context.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/effect_context.h" 4#include "audio_core/renderer/effect/effect_context.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void EffectContext::Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, 8void EffectContext::Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_,
9 std::span<EffectResultState> result_states_cpu_, 9 std::span<EffectResultState> result_states_cpu_,
@@ -38,4 +38,4 @@ void EffectContext::UpdateStateByDspShared() {
38 } 38 }
39} 39}
40 40
41} // namespace AudioCore::AudioRenderer 41} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/effect_context.h b/src/audio_core/renderer/effect/effect_context.h
index 8f6d6e7d8..8364c5521 100644
--- a/src/audio_core/renderer/effect/effect_context.h
+++ b/src/audio_core/renderer/effect/effect_context.h
@@ -9,7 +9,7 @@
9#include "audio_core/renderer/effect/effect_result_state.h" 9#include "audio_core/renderer/effect/effect_result_state.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13 13
14class EffectContext { 14class EffectContext {
15public: 15public:
@@ -72,4 +72,4 @@ private:
72 size_t dsp_state_count{}; 72 size_t dsp_state_count{};
73}; 73};
74 74
75} // namespace AudioCore::AudioRenderer 75} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h
index dbdccf278..b49503409 100644
--- a/src/audio_core/renderer/effect/effect_info_base.h
+++ b/src/audio_core/renderer/effect/effect_info_base.h
@@ -12,7 +12,7 @@
12#include "audio_core/renderer/memory/pool_mapper.h" 12#include "audio_core/renderer/memory/pool_mapper.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14 14
15namespace AudioCore::AudioRenderer { 15namespace AudioCore::Renderer {
16/** 16/**
17 * Base of all effects. Holds various data and functions used for all derived effects. 17 * Base of all effects. Holds various data and functions used for all derived effects.
18 * Should not be used directly. 18 * Should not be used directly.
@@ -432,4 +432,4 @@ protected:
432 std::array<u8, sizeof(State)> state{}; 432 std::array<u8, sizeof(State)> state{};
433}; 433};
434 434
435} // namespace AudioCore::AudioRenderer 435} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/effect_reset.h b/src/audio_core/renderer/effect/effect_reset.h
index 1ea67e334..c9e3b4b78 100644
--- a/src/audio_core/renderer/effect/effect_reset.h
+++ b/src/audio_core/renderer/effect/effect_reset.h
@@ -14,7 +14,7 @@
14#include "audio_core/renderer/effect/reverb.h" 14#include "audio_core/renderer/effect/reverb.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16 16
17namespace AudioCore::AudioRenderer { 17namespace AudioCore::Renderer {
18/** 18/**
19 * Reset an effect, and create a new one of the given type. 19 * Reset an effect, and create a new one of the given type.
20 * 20 *
@@ -68,4 +68,4 @@ static void ResetEffect(EffectInfoBase* effect, const EffectInfoBase::Type type)
68 } 68 }
69} 69}
70 70
71} // namespace AudioCore::AudioRenderer 71} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/effect_result_state.h b/src/audio_core/renderer/effect/effect_result_state.h
index ae096ad69..f4d4b6086 100644
--- a/src/audio_core/renderer/effect/effect_result_state.h
+++ b/src/audio_core/renderer/effect/effect_result_state.h
@@ -7,10 +7,10 @@
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12struct EffectResultState { 12struct EffectResultState {
13 std::array<u8, 0x80> state; 13 std::array<u8, 0x80> state;
14}; 14};
15 15
16} // namespace AudioCore::AudioRenderer 16} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/i3dl2.cpp b/src/audio_core/renderer/effect/i3dl2.cpp
index 960b29cfc..a3c324c1e 100644
--- a/src/audio_core/renderer/effect/i3dl2.cpp
+++ b/src/audio_core/renderer/effect/i3dl2.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/i3dl2.h" 4#include "audio_core/renderer/effect/i3dl2.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, 8void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info,
9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { 9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
@@ -91,4 +91,4 @@ CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) {
91 return GetSingleBuffer(index); 91 return GetSingleBuffer(index);
92} 92}
93 93
94} // namespace AudioCore::AudioRenderer 94} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/i3dl2.h b/src/audio_core/renderer/effect/i3dl2.h
index 6e3ffd1d4..e0432b4ae 100644
--- a/src/audio_core/renderer/effect/i3dl2.h
+++ b/src/audio_core/renderer/effect/i3dl2.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/fixed_point.h" 12#include "common/fixed_point.h"
13 13
14namespace AudioCore::AudioRenderer { 14namespace AudioCore::Renderer {
15 15
16class I3dl2ReverbInfo : public EffectInfoBase { 16class I3dl2ReverbInfo : public EffectInfoBase {
17public: 17public:
@@ -198,4 +198,4 @@ public:
198 CpuAddr GetWorkbuffer(s32 index) override; 198 CpuAddr GetWorkbuffer(s32 index) override;
199}; 199};
200 200
201} // namespace AudioCore::AudioRenderer 201} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/light_limiter.cpp b/src/audio_core/renderer/effect/light_limiter.cpp
index 1635a952d..9c8ea3c49 100644
--- a/src/audio_core/renderer/effect/light_limiter.cpp
+++ b/src/audio_core/renderer/effect/light_limiter.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/light_limiter.h" 4#include "audio_core/renderer/effect/light_limiter.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, 8void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { 9 const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
@@ -78,4 +78,4 @@ CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) {
78 return GetSingleBuffer(index); 78 return GetSingleBuffer(index);
79} 79}
80 80
81} // namespace AudioCore::AudioRenderer 81} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/light_limiter.h b/src/audio_core/renderer/effect/light_limiter.h
index 338d67bbc..7f2ede405 100644
--- a/src/audio_core/renderer/effect/light_limiter.h
+++ b/src/audio_core/renderer/effect/light_limiter.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/fixed_point.h" 12#include "common/fixed_point.h"
13 13
14namespace AudioCore::AudioRenderer { 14namespace AudioCore::Renderer {
15 15
16class LightLimiterInfo : public EffectInfoBase { 16class LightLimiterInfo : public EffectInfoBase {
17public: 17public:
@@ -135,4 +135,4 @@ public:
135 CpuAddr GetWorkbuffer(s32 index) override; 135 CpuAddr GetWorkbuffer(s32 index) override;
136}; 136};
137 137
138} // namespace AudioCore::AudioRenderer 138} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/reverb.cpp b/src/audio_core/renderer/effect/reverb.cpp
index 2d32383d0..4da72469a 100644
--- a/src/audio_core/renderer/effect/reverb.cpp
+++ b/src/audio_core/renderer/effect/reverb.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/effect/reverb.h" 4#include "audio_core/renderer/effect/reverb.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, 8void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
9 const PoolMapper& pool_mapper) { 9 const PoolMapper& pool_mapper) {
@@ -90,4 +90,4 @@ CpuAddr ReverbInfo::GetWorkbuffer(s32 index) {
90 return GetSingleBuffer(index); 90 return GetSingleBuffer(index);
91} 91}
92 92
93} // namespace AudioCore::AudioRenderer 93} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/effect/reverb.h b/src/audio_core/renderer/effect/reverb.h
index 6cc345ef6..52a048da6 100644
--- a/src/audio_core/renderer/effect/reverb.h
+++ b/src/audio_core/renderer/effect/reverb.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/fixed_point.h" 12#include "common/fixed_point.h"
13 13
14namespace AudioCore::AudioRenderer { 14namespace AudioCore::Renderer {
15 15
16class ReverbInfo : public EffectInfoBase { 16class ReverbInfo : public EffectInfoBase {
17public: 17public:
@@ -187,4 +187,4 @@ public:
187 CpuAddr GetWorkbuffer(s32 index) override; 187 CpuAddr GetWorkbuffer(s32 index) override;
188}; 188};
189 189
190} // namespace AudioCore::AudioRenderer 190} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/memory/address_info.h b/src/audio_core/renderer/memory/address_info.h
index bb5c930e1..c81ef1b8a 100644
--- a/src/audio_core/renderer/memory/address_info.h
+++ b/src/audio_core/renderer/memory/address_info.h
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/memory/memory_pool_info.h" 6#include "audio_core/renderer/memory/memory_pool_info.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11/** 11/**
12 * Represents a region of mapped or unmapped memory. 12 * Represents a region of mapped or unmapped memory.
@@ -121,4 +121,4 @@ private:
121 CpuAddr dsp_address; 121 CpuAddr dsp_address;
122}; 122};
123 123
124} // namespace AudioCore::AudioRenderer 124} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/memory/memory_pool_info.cpp b/src/audio_core/renderer/memory/memory_pool_info.cpp
index 9b7824af1..03b44d5f3 100644
--- a/src/audio_core/renderer/memory/memory_pool_info.cpp
+++ b/src/audio_core/renderer/memory/memory_pool_info.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/memory/memory_pool_info.h" 4#include "audio_core/renderer/memory/memory_pool_info.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8CpuAddr MemoryPoolInfo::GetCpuAddress() const { 8CpuAddr MemoryPoolInfo::GetCpuAddress() const {
9 return cpu_address; 9 return cpu_address;
@@ -58,4 +58,4 @@ bool MemoryPoolInfo::IsUsed() const {
58 return in_use; 58 return in_use;
59} 59}
60 60
61} // namespace AudioCore::AudioRenderer 61} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/memory/memory_pool_info.h b/src/audio_core/renderer/memory/memory_pool_info.h
index 80c571bc1..2f9c85184 100644
--- a/src/audio_core/renderer/memory/memory_pool_info.h
+++ b/src/audio_core/renderer/memory/memory_pool_info.h
@@ -8,7 +8,7 @@
8#include "audio_core/common/common.h" 8#include "audio_core/common/common.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::Renderer {
12/** 12/**
13 * CPU pools are mapped in user memory with the supplied process_handle (see PoolMapper). 13 * CPU pools are mapped in user memory with the supplied process_handle (see PoolMapper).
14 */ 14 */
@@ -167,4 +167,4 @@ private:
167 bool in_use{}; 167 bool in_use{};
168}; 168};
169 169
170} // namespace AudioCore::AudioRenderer 170} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/memory/pool_mapper.cpp b/src/audio_core/renderer/memory/pool_mapper.cpp
index 7fd2b5f47..999bb746b 100644
--- a/src/audio_core/renderer/memory/pool_mapper.cpp
+++ b/src/audio_core/renderer/memory/pool_mapper.cpp
@@ -6,7 +6,7 @@
6#include "core/hle/kernel/k_process.h" 6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/svc.h" 7#include "core/hle/kernel/svc.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11PoolMapper::PoolMapper(u32 process_handle_, bool force_map_) 11PoolMapper::PoolMapper(u32 process_handle_, bool force_map_)
12 : process_handle{process_handle_}, force_map{force_map_} {} 12 : process_handle{process_handle_}, force_map{force_map_} {}
@@ -240,4 +240,4 @@ bool PoolMapper::InitializeSystemPool(MemoryPoolInfo& pool, const u8* memory,
240 } 240 }
241} 241}
242 242
243} // namespace AudioCore::AudioRenderer 243} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/memory/pool_mapper.h b/src/audio_core/renderer/memory/pool_mapper.h
index 9a691da7a..95ae5d8ea 100644
--- a/src/audio_core/renderer/memory/pool_mapper.h
+++ b/src/audio_core/renderer/memory/pool_mapper.h
@@ -10,7 +10,7 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/service/audio/errors.h" 11#include "core/hle/service/audio/errors.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14class AddressInfo; 14class AddressInfo;
15 15
16/** 16/**
@@ -176,4 +176,4 @@ private:
176 bool force_map; 176 bool force_map;
177}; 177};
178 178
179} // namespace AudioCore::AudioRenderer 179} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp
index 3a18ae7c2..c712610bb 100644
--- a/src/audio_core/renderer/mix/mix_context.cpp
+++ b/src/audio_core/renderer/mix/mix_context.cpp
@@ -7,7 +7,7 @@
7#include "audio_core/renderer/splitter/splitter_context.h" 7#include "audio_core/renderer/splitter/splitter_context.h"
8#include "common/polyfill_ranges.h" 8#include "common/polyfill_ranges.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12void MixContext::Initialize(std::span<MixInfo*> sorted_mix_infos_, std::span<MixInfo> mix_infos_, 12void MixContext::Initialize(std::span<MixInfo*> sorted_mix_infos_, std::span<MixInfo> mix_infos_,
13 const u32 count_, std::span<s32> effect_process_order_buffer_, 13 const u32 count_, std::span<s32> effect_process_order_buffer_,
@@ -139,4 +139,4 @@ EdgeMatrix& MixContext::GetEdgeMatrix() {
139 return edge_matrix; 139 return edge_matrix;
140} 140}
141 141
142} // namespace AudioCore::AudioRenderer 142} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/mix/mix_context.h b/src/audio_core/renderer/mix/mix_context.h
index bcd9637da..ce19ec8d6 100644
--- a/src/audio_core/renderer/mix/mix_context.h
+++ b/src/audio_core/renderer/mix/mix_context.h
@@ -10,7 +10,7 @@
10#include "audio_core/renderer/nodes/node_states.h" 10#include "audio_core/renderer/nodes/node_states.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14class SplitterContext; 14class SplitterContext;
15 15
16/* 16/*
@@ -121,4 +121,4 @@ private:
121 EdgeMatrix edge_matrix{}; 121 EdgeMatrix edge_matrix{};
122}; 122};
123 123
124} // namespace AudioCore::AudioRenderer 124} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/mix/mix_info.cpp b/src/audio_core/renderer/mix/mix_info.cpp
index cc18e57ee..5e44bde18 100644
--- a/src/audio_core/renderer/mix/mix_info.cpp
+++ b/src/audio_core/renderer/mix/mix_info.cpp
@@ -7,7 +7,7 @@
7#include "audio_core/renderer/nodes/edge_matrix.h" 7#include "audio_core/renderer/nodes/edge_matrix.h"
8#include "audio_core/renderer/splitter/splitter_context.h" 8#include "audio_core/renderer/splitter/splitter_context.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12MixInfo::MixInfo(std::span<s32> effect_order_buffer_, s32 effect_count_, BehaviorInfo& behavior) 12MixInfo::MixInfo(std::span<s32> effect_order_buffer_, s32 effect_count_, BehaviorInfo& behavior)
13 : effect_order_buffer{effect_order_buffer_}, effect_count{effect_count_}, 13 : effect_order_buffer{effect_order_buffer_}, effect_count{effect_count_},
@@ -117,4 +117,4 @@ bool MixInfo::HasAnyConnection() const {
117 return dst_mix_id != UnusedMixId || dst_splitter_id != UnusedSplitterId; 117 return dst_mix_id != UnusedMixId || dst_splitter_id != UnusedSplitterId;
118} 118}
119 119
120} // namespace AudioCore::AudioRenderer 120} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/mix/mix_info.h b/src/audio_core/renderer/mix/mix_info.h
index b5fa4c0c7..7005daa4f 100644
--- a/src/audio_core/renderer/mix/mix_info.h
+++ b/src/audio_core/renderer/mix/mix_info.h
@@ -9,7 +9,7 @@
9#include "audio_core/common/common.h" 9#include "audio_core/common/common.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13class EdgeMatrix; 13class EdgeMatrix;
14class SplitterContext; 14class SplitterContext;
15class EffectContext; 15class EffectContext;
@@ -121,4 +121,4 @@ public:
121 const bool long_size_pre_delay_supported; 121 const bool long_size_pre_delay_supported;
122}; 122};
123 123
124} // namespace AudioCore::AudioRenderer 124} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/nodes/bit_array.h b/src/audio_core/renderer/nodes/bit_array.h
index b0d53cd51..d8a2d09d0 100644
--- a/src/audio_core/renderer/nodes/bit_array.h
+++ b/src/audio_core/renderer/nodes/bit_array.h
@@ -7,7 +7,7 @@
7 7
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11/** 11/**
12 * Represents an array of bits used for nodes and edges for the mixing graph. 12 * Represents an array of bits used for nodes and edges for the mixing graph.
13 */ 13 */
@@ -22,4 +22,4 @@ struct BitArray {
22 u32 size{}; 22 u32 size{};
23}; 23};
24 24
25} // namespace AudioCore::AudioRenderer 25} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/nodes/edge_matrix.cpp b/src/audio_core/renderer/nodes/edge_matrix.cpp
index 5573f33b9..c28773b22 100644
--- a/src/audio_core/renderer/nodes/edge_matrix.cpp
+++ b/src/audio_core/renderer/nodes/edge_matrix.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/nodes/edge_matrix.h" 4#include "audio_core/renderer/nodes/edge_matrix.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void EdgeMatrix::Initialize([[maybe_unused]] std::span<u8> buffer, 8void EdgeMatrix::Initialize([[maybe_unused]] std::span<u8> buffer,
9 [[maybe_unused]] const u64 node_buffer_size, const u32 count_) { 9 [[maybe_unused]] const u64 node_buffer_size, const u32 count_) {
@@ -35,4 +35,4 @@ u32 EdgeMatrix::GetNodeCount() const {
35 return count; 35 return count;
36} 36}
37 37
38} // namespace AudioCore::AudioRenderer 38} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/nodes/edge_matrix.h b/src/audio_core/renderer/nodes/edge_matrix.h
index 27a20e43e..0271c23b1 100644
--- a/src/audio_core/renderer/nodes/edge_matrix.h
+++ b/src/audio_core/renderer/nodes/edge_matrix.h
@@ -9,7 +9,7 @@
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13/** 13/**
14 * An edge matrix, holding the connections for each node to every other node in the graph. 14 * An edge matrix, holding the connections for each node to every other node in the graph.
15 */ 15 */
@@ -79,4 +79,4 @@ private:
79 u32 count; 79 u32 count;
80}; 80};
81 81
82} // namespace AudioCore::AudioRenderer 82} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/nodes/node_states.cpp b/src/audio_core/renderer/nodes/node_states.cpp
index b7a44a54c..028a58041 100644
--- a/src/audio_core/renderer/nodes/node_states.cpp
+++ b/src/audio_core/renderer/nodes/node_states.cpp
@@ -4,7 +4,7 @@
4#include "audio_core/renderer/nodes/node_states.h" 4#include "audio_core/renderer/nodes/node_states.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8 8
9void NodeStates::Initialize(std::span<u8> buffer_, [[maybe_unused]] const u64 node_buffer_size, 9void NodeStates::Initialize(std::span<u8> buffer_, [[maybe_unused]] const u64 node_buffer_size,
10 const u32 count) { 10 const u32 count) {
@@ -138,4 +138,4 @@ std::pair<std::span<u32>::reverse_iterator, size_t> NodeStates::GetSortedResuls(
138 return {results.rbegin(), result_pos}; 138 return {results.rbegin(), result_pos};
139} 139}
140 140
141} // namespace AudioCore::AudioRenderer 141} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/nodes/node_states.h b/src/audio_core/renderer/nodes/node_states.h
index e768cd4b5..991a82841 100644
--- a/src/audio_core/renderer/nodes/node_states.h
+++ b/src/audio_core/renderer/nodes/node_states.h
@@ -10,7 +10,7 @@
10#include "common/alignment.h" 10#include "common/alignment.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14/** 14/**
15 * Graph utility functions for sorting and getting results from the DAG. 15 * Graph utility functions for sorting and getting results from the DAG.
16 */ 16 */
@@ -192,4 +192,4 @@ private:
192 Stack stack{}; 192 Stack stack{};
193}; 193};
194 194
195} // namespace AudioCore::AudioRenderer 195} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/detail_aspect.cpp b/src/audio_core/renderer/performance/detail_aspect.cpp
index f6405937f..ef8b47cee 100644
--- a/src/audio_core/renderer/performance/detail_aspect.cpp
+++ b/src/audio_core/renderer/performance/detail_aspect.cpp
@@ -5,7 +5,7 @@
5#include "audio_core/renderer/command/command_generator.h" 5#include "audio_core/renderer/command/command_generator.h"
6#include "audio_core/renderer/performance/detail_aspect.h" 6#include "audio_core/renderer/performance/detail_aspect.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10DetailAspect::DetailAspect(CommandGenerator& command_generator_, 10DetailAspect::DetailAspect(CommandGenerator& command_generator_,
11 const PerformanceEntryType entry_type, const s32 node_id_, 11 const PerformanceEntryType entry_type, const s32 node_id_,
@@ -22,4 +22,4 @@ DetailAspect::DetailAspect(CommandGenerator& command_generator_,
22 } 22 }
23} 23}
24 24
25} // namespace AudioCore::AudioRenderer 25} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/detail_aspect.h b/src/audio_core/renderer/performance/detail_aspect.h
index 736c331b9..0bd7f80c8 100644
--- a/src/audio_core/renderer/performance/detail_aspect.h
+++ b/src/audio_core/renderer/performance/detail_aspect.h
@@ -7,7 +7,7 @@
7#include "audio_core/renderer/performance/performance_manager.h" 7#include "audio_core/renderer/performance/performance_manager.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11class CommandGenerator; 11class CommandGenerator;
12 12
13/** 13/**
@@ -29,4 +29,4 @@ public:
29 s32 node_id; 29 s32 node_id;
30}; 30};
31 31
32} // namespace AudioCore::AudioRenderer 32} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/entry_aspect.cpp b/src/audio_core/renderer/performance/entry_aspect.cpp
index dd4165803..c9241a639 100644
--- a/src/audio_core/renderer/performance/entry_aspect.cpp
+++ b/src/audio_core/renderer/performance/entry_aspect.cpp
@@ -5,7 +5,7 @@
5#include "audio_core/renderer/command/command_generator.h" 5#include "audio_core/renderer/command/command_generator.h"
6#include "audio_core/renderer/performance/entry_aspect.h" 6#include "audio_core/renderer/performance/entry_aspect.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10EntryAspect::EntryAspect(CommandGenerator& command_generator_, const PerformanceEntryType type, 10EntryAspect::EntryAspect(CommandGenerator& command_generator_, const PerformanceEntryType type,
11 const s32 node_id_) 11 const s32 node_id_)
@@ -20,4 +20,4 @@ EntryAspect::EntryAspect(CommandGenerator& command_generator_, const Performance
20 } 20 }
21} 21}
22 22
23} // namespace AudioCore::AudioRenderer 23} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/entry_aspect.h b/src/audio_core/renderer/performance/entry_aspect.h
index 14c9e3baf..f99287d68 100644
--- a/src/audio_core/renderer/performance/entry_aspect.h
+++ b/src/audio_core/renderer/performance/entry_aspect.h
@@ -7,7 +7,7 @@
7#include "audio_core/renderer/performance/performance_manager.h" 7#include "audio_core/renderer/performance/performance_manager.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11class CommandGenerator; 11class CommandGenerator;
12 12
13/** 13/**
@@ -28,4 +28,4 @@ public:
28 s32 node_id; 28 s32 node_id;
29}; 29};
30 30
31} // namespace AudioCore::AudioRenderer 31} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/performance_detail.h b/src/audio_core/renderer/performance/performance_detail.h
index f603b9026..2b0cf9422 100644
--- a/src/audio_core/renderer/performance/performance_detail.h
+++ b/src/audio_core/renderer/performance/performance_detail.h
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/performance/performance_entry.h" 6#include "audio_core/renderer/performance/performance_entry.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11enum class PerformanceDetailType : u8 { 11enum class PerformanceDetailType : u8 {
12 Invalid, 12 Invalid,
@@ -47,4 +47,4 @@ struct PerformanceDetailVersion2 {
47static_assert(sizeof(PerformanceDetailVersion2) == 0x18, 47static_assert(sizeof(PerformanceDetailVersion2) == 0x18,
48 "PerformanceDetailVersion2 has the wrong size!"); 48 "PerformanceDetailVersion2 has the wrong size!");
49 49
50} // namespace AudioCore::AudioRenderer 50} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/performance_entry.h b/src/audio_core/renderer/performance/performance_entry.h
index d6b1158db..dbd6053a5 100644
--- a/src/audio_core/renderer/performance/performance_entry.h
+++ b/src/audio_core/renderer/performance/performance_entry.h
@@ -5,7 +5,7 @@
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10enum class PerformanceEntryType : u8 { 10enum class PerformanceEntryType : u8 {
11 Invalid, 11 Invalid,
@@ -34,4 +34,4 @@ struct PerformanceEntryVersion2 {
34static_assert(sizeof(PerformanceEntryVersion2) == 0x18, 34static_assert(sizeof(PerformanceEntryVersion2) == 0x18,
35 "PerformanceEntryVersion2 has the wrong size!"); 35 "PerformanceEntryVersion2 has the wrong size!");
36 36
37} // namespace AudioCore::AudioRenderer 37} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/performance_entry_addresses.h b/src/audio_core/renderer/performance/performance_entry_addresses.h
index e381d765c..51eee975f 100644
--- a/src/audio_core/renderer/performance/performance_entry_addresses.h
+++ b/src/audio_core/renderer/performance/performance_entry_addresses.h
@@ -5,7 +5,7 @@
5 5
6#include "audio_core/common/common.h" 6#include "audio_core/common/common.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10struct PerformanceEntryAddresses { 10struct PerformanceEntryAddresses {
11 CpuAddr translated_address; 11 CpuAddr translated_address;
@@ -14,4 +14,4 @@ struct PerformanceEntryAddresses {
14 CpuAddr entry_processed_time_offset; 14 CpuAddr entry_processed_time_offset;
15}; 15};
16 16
17} // namespace AudioCore::AudioRenderer 17} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/performance_frame_header.h b/src/audio_core/renderer/performance/performance_frame_header.h
index b1848284e..24e4989f8 100644
--- a/src/audio_core/renderer/performance/performance_frame_header.h
+++ b/src/audio_core/renderer/performance/performance_frame_header.h
@@ -5,7 +5,7 @@
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10struct PerformanceFrameHeaderVersion1 { 10struct PerformanceFrameHeaderVersion1 {
11 /* 0x00 */ u32 magic; // "PERF" 11 /* 0x00 */ u32 magic; // "PERF"
@@ -33,4 +33,4 @@ struct PerformanceFrameHeaderVersion2 {
33static_assert(sizeof(PerformanceFrameHeaderVersion2) == 0x30, 33static_assert(sizeof(PerformanceFrameHeaderVersion2) == 0x30,
34 "PerformanceFrameHeaderVersion2 has the wrong size!"); 34 "PerformanceFrameHeaderVersion2 has the wrong size!");
35 35
36} // namespace AudioCore::AudioRenderer 36} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp
index 8aa0f5ed0..ce736db71 100644
--- a/src/audio_core/renderer/performance/performance_manager.cpp
+++ b/src/audio_core/renderer/performance/performance_manager.cpp
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/performance/performance_manager.h" 6#include "audio_core/renderer/performance/performance_manager.h"
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11void PerformanceManager::CreateImpl(const size_t version) { 11void PerformanceManager::CreateImpl(const size_t version) {
12 switch (version) { 12 switch (version) {
@@ -643,4 +643,4 @@ void PerformanceManagerImpl<PerformanceVersion::Version2, PerformanceFrameHeader
643 target_node_id = target_node_id_; 643 target_node_id = target_node_id_;
644} 644}
645 645
646} // namespace AudioCore::AudioRenderer 646} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/performance/performance_manager.h b/src/audio_core/renderer/performance/performance_manager.h
index b65caa9b6..ffd0fa1fb 100644
--- a/src/audio_core/renderer/performance/performance_manager.h
+++ b/src/audio_core/renderer/performance/performance_manager.h
@@ -14,7 +14,7 @@
14#include "audio_core/renderer/performance/performance_frame_header.h" 14#include "audio_core/renderer/performance/performance_frame_header.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16 16
17namespace AudioCore::AudioRenderer { 17namespace AudioCore::Renderer {
18class BehaviorInfo; 18class BehaviorInfo;
19class MemoryPoolInfo; 19class MemoryPoolInfo;
20 20
@@ -272,4 +272,4 @@ private:
272 PerformanceVersion version{}; 272 PerformanceVersion version{};
273}; 273};
274 274
275} // namespace AudioCore::AudioRenderer 275} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp b/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp
index d91f10402..0ede02b6b 100644
--- a/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp
+++ b/src/audio_core/renderer/sink/circular_buffer_sink_info.cpp
@@ -5,7 +5,7 @@
5#include "audio_core/renderer/sink/circular_buffer_sink_info.h" 5#include "audio_core/renderer/sink/circular_buffer_sink_info.h"
6#include "audio_core/renderer/upsampler/upsampler_manager.h" 6#include "audio_core/renderer/upsampler/upsampler_manager.h"
7 7
8namespace AudioCore::AudioRenderer { 8namespace AudioCore::Renderer {
9 9
10CircularBufferSinkInfo::CircularBufferSinkInfo() { 10CircularBufferSinkInfo::CircularBufferSinkInfo() {
11 state.fill(0); 11 state.fill(0);
@@ -73,4 +73,4 @@ void CircularBufferSinkInfo::UpdateForCommandGeneration() {
73 } 73 }
74} 74}
75 75
76} // namespace AudioCore::AudioRenderer 76} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/circular_buffer_sink_info.h b/src/audio_core/renderer/sink/circular_buffer_sink_info.h
index 3356213ea..d4e61d641 100644
--- a/src/audio_core/renderer/sink/circular_buffer_sink_info.h
+++ b/src/audio_core/renderer/sink/circular_buffer_sink_info.h
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/sink/sink_info_base.h" 6#include "audio_core/renderer/sink/sink_info_base.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10/** 10/**
11 * Info for a circular buffer sink. 11 * Info for a circular buffer sink.
12 */ 12 */
@@ -38,4 +38,4 @@ public:
38static_assert(sizeof(CircularBufferSinkInfo) <= sizeof(SinkInfoBase), 38static_assert(sizeof(CircularBufferSinkInfo) <= sizeof(SinkInfoBase),
39 "CircularBufferSinkInfo is too large!"); 39 "CircularBufferSinkInfo is too large!");
40 40
41} // namespace AudioCore::AudioRenderer 41} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/device_sink_info.cpp b/src/audio_core/renderer/sink/device_sink_info.cpp
index b7b3d6f1d..2de05e38e 100644
--- a/src/audio_core/renderer/sink/device_sink_info.cpp
+++ b/src/audio_core/renderer/sink/device_sink_info.cpp
@@ -4,7 +4,7 @@
4#include "audio_core/renderer/sink/device_sink_info.h" 4#include "audio_core/renderer/sink/device_sink_info.h"
5#include "audio_core/renderer/upsampler/upsampler_manager.h" 5#include "audio_core/renderer/upsampler/upsampler_manager.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8 8
9DeviceSinkInfo::DeviceSinkInfo() { 9DeviceSinkInfo::DeviceSinkInfo() {
10 state.fill(0); 10 state.fill(0);
@@ -54,4 +54,4 @@ void DeviceSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStatus& out_
54 54
55void DeviceSinkInfo::UpdateForCommandGeneration() {} 55void DeviceSinkInfo::UpdateForCommandGeneration() {}
56 56
57} // namespace AudioCore::AudioRenderer 57} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/device_sink_info.h b/src/audio_core/renderer/sink/device_sink_info.h
index a1c441454..7974ae820 100644
--- a/src/audio_core/renderer/sink/device_sink_info.h
+++ b/src/audio_core/renderer/sink/device_sink_info.h
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/sink/sink_info_base.h" 6#include "audio_core/renderer/sink/sink_info_base.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10/** 10/**
11 * Info for a device sink. 11 * Info for a device sink.
12 */ 12 */
@@ -37,4 +37,4 @@ public:
37}; 37};
38static_assert(sizeof(DeviceSinkInfo) <= sizeof(SinkInfoBase), "DeviceSinkInfo is too large!"); 38static_assert(sizeof(DeviceSinkInfo) <= sizeof(SinkInfoBase), "DeviceSinkInfo is too large!");
39 39
40} // namespace AudioCore::AudioRenderer 40} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/sink_context.cpp b/src/audio_core/renderer/sink/sink_context.cpp
index 634bc1cf9..a4f9cac21 100644
--- a/src/audio_core/renderer/sink/sink_context.cpp
+++ b/src/audio_core/renderer/sink/sink_context.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/sink/sink_context.h" 4#include "audio_core/renderer/sink/sink_context.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8void SinkContext::Initialize(std::span<SinkInfoBase> sink_infos_, const u32 sink_count_) { 8void SinkContext::Initialize(std::span<SinkInfoBase> sink_infos_, const u32 sink_count_) {
9 sink_infos = sink_infos_; 9 sink_infos = sink_infos_;
@@ -18,4 +18,4 @@ u32 SinkContext::GetCount() const {
18 return sink_count; 18 return sink_count;
19} 19}
20 20
21} // namespace AudioCore::AudioRenderer 21} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/sink_context.h b/src/audio_core/renderer/sink/sink_context.h
index 185572e29..66925b48e 100644
--- a/src/audio_core/renderer/sink/sink_context.h
+++ b/src/audio_core/renderer/sink/sink_context.h
@@ -8,7 +8,7 @@
8#include "audio_core/renderer/sink/sink_info_base.h" 8#include "audio_core/renderer/sink/sink_info_base.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::Renderer {
12/** 12/**
13 * Manages output sinks. 13 * Manages output sinks.
14 */ 14 */
@@ -44,4 +44,4 @@ private:
44 u32 sink_count{}; 44 u32 sink_count{};
45}; 45};
46 46
47} // namespace AudioCore::AudioRenderer 47} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/sink_info_base.cpp b/src/audio_core/renderer/sink/sink_info_base.cpp
index 4279beaa0..8a064f15a 100644
--- a/src/audio_core/renderer/sink/sink_info_base.cpp
+++ b/src/audio_core/renderer/sink/sink_info_base.cpp
@@ -4,7 +4,7 @@
4#include "audio_core/renderer/memory/pool_mapper.h" 4#include "audio_core/renderer/memory/pool_mapper.h"
5#include "audio_core/renderer/sink/sink_info_base.h" 5#include "audio_core/renderer/sink/sink_info_base.h"
6 6
7namespace AudioCore::AudioRenderer { 7namespace AudioCore::Renderer {
8 8
9void SinkInfoBase::CleanUp() { 9void SinkInfoBase::CleanUp() {
10 type = Type::Invalid; 10 type = Type::Invalid;
@@ -48,4 +48,4 @@ u8* SinkInfoBase::GetParameter() {
48 return parameter.data(); 48 return parameter.data();
49} 49}
50 50
51} // namespace AudioCore::AudioRenderer 51} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/sink/sink_info_base.h b/src/audio_core/renderer/sink/sink_info_base.h
index a1b855f20..e10d1cb38 100644
--- a/src/audio_core/renderer/sink/sink_info_base.h
+++ b/src/audio_core/renderer/sink/sink_info_base.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/fixed_point.h" 12#include "common/fixed_point.h"
13 13
14namespace AudioCore::AudioRenderer { 14namespace AudioCore::Renderer {
15struct UpsamplerInfo; 15struct UpsamplerInfo;
16class PoolMapper; 16class PoolMapper;
17 17
@@ -174,4 +174,4 @@ protected:
174 parameter{}; 174 parameter{};
175}; 175};
176 176
177} // namespace AudioCore::AudioRenderer 177} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp
index 7a23ba43f..686150ea6 100644
--- a/src/audio_core/renderer/splitter/splitter_context.cpp
+++ b/src/audio_core/renderer/splitter/splitter_context.cpp
@@ -7,7 +7,7 @@
7#include "audio_core/renderer/splitter/splitter_context.h" 7#include "audio_core/renderer/splitter/splitter_context.h"
8#include "common/alignment.h" 8#include "common/alignment.h"
9 9
10namespace AudioCore::AudioRenderer { 10namespace AudioCore::Renderer {
11 11
12SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id, 12SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id,
13 const s32 destination_id) { 13 const s32 destination_id) {
@@ -214,4 +214,4 @@ u64 SplitterContext::CalcWorkBufferSize(const BehaviorInfo& behavior,
214 return size; 214 return size;
215} 215}
216 216
217} // namespace AudioCore::AudioRenderer 217} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h
index 1a63db1d3..556e6dcc3 100644
--- a/src/audio_core/renderer/splitter/splitter_context.h
+++ b/src/audio_core/renderer/splitter/splitter_context.h
@@ -13,7 +13,7 @@ namespace AudioCore {
13struct AudioRendererParameterInternal; 13struct AudioRendererParameterInternal;
14class WorkbufferAllocator; 14class WorkbufferAllocator;
15 15
16namespace AudioRenderer { 16namespace Renderer {
17class BehaviorInfo; 17class BehaviorInfo;
18 18
19/** 19/**
@@ -185,5 +185,5 @@ private:
185 bool splitter_bug_fixed{}; 185 bool splitter_bug_fixed{};
186}; 186};
187 187
188} // namespace AudioRenderer 188} // namespace Renderer
189} // namespace AudioCore 189} // namespace AudioCore
diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp
index b27d44896..5ec37e48e 100644
--- a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp
+++ b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/splitter/splitter_destinations_data.h" 4#include "audio_core/renderer/splitter/splitter_destinations_data.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8SplitterDestinationData::SplitterDestinationData(const s32 id_) : id{id_} {} 8SplitterDestinationData::SplitterDestinationData(const s32 id_) : id{id_} {}
9 9
@@ -84,4 +84,4 @@ void SplitterDestinationData::SetNext(SplitterDestinationData* next_) {
84 next = next_; 84 next = next_;
85} 85}
86 86
87} // namespace AudioCore::AudioRenderer 87} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.h b/src/audio_core/renderer/splitter/splitter_destinations_data.h
index d55ce0ad3..90edfc667 100644
--- a/src/audio_core/renderer/splitter/splitter_destinations_data.h
+++ b/src/audio_core/renderer/splitter/splitter_destinations_data.h
@@ -9,7 +9,7 @@
9#include "audio_core/common/common.h" 9#include "audio_core/common/common.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13/** 13/**
14 * Represents a mixing node, can be connected to a previous and next destination forming a chain 14 * Represents a mixing node, can be connected to a previous and next destination forming a chain
15 * that a certain mix buffer will pass through to output. 15 * that a certain mix buffer will pass through to output.
@@ -132,4 +132,4 @@ private:
132 bool need_update{}; 132 bool need_update{};
133}; 133};
134 134
135} // namespace AudioCore::AudioRenderer 135} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/splitter/splitter_info.cpp b/src/audio_core/renderer/splitter/splitter_info.cpp
index 1aee6720b..beb5b7f19 100644
--- a/src/audio_core/renderer/splitter/splitter_info.cpp
+++ b/src/audio_core/renderer/splitter/splitter_info.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/splitter/splitter_info.h" 4#include "audio_core/renderer/splitter/splitter_info.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8SplitterInfo::SplitterInfo(const s32 id_) : id{id_} {} 8SplitterInfo::SplitterInfo(const s32 id_) : id{id_} {}
9 9
@@ -76,4 +76,4 @@ void SplitterInfo::SetDestinations(SplitterDestinationData* destinations_) {
76 destinations = destinations_; 76 destinations = destinations_;
77} 77}
78 78
79} // namespace AudioCore::AudioRenderer 79} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/splitter/splitter_info.h b/src/audio_core/renderer/splitter/splitter_info.h
index b0ad01fe0..c1e4c2df1 100644
--- a/src/audio_core/renderer/splitter/splitter_info.h
+++ b/src/audio_core/renderer/splitter/splitter_info.h
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/splitter/splitter_destinations_data.h" 6#include "audio_core/renderer/splitter/splitter_destinations_data.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10/** 10/**
11 * Represents a splitter, wraps multiple output destinations to split an input mix into. 11 * Represents a splitter, wraps multiple output destinations to split an input mix into.
12 */ 12 */
@@ -104,4 +104,4 @@ private:
104 u32 channel_count{}; 104 u32 channel_count{};
105}; 105};
106 106
107} // namespace AudioCore::AudioRenderer 107} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index a23627472..d29754634 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -4,12 +4,13 @@
4#include <chrono> 4#include <chrono>
5#include <span> 5#include <span>
6 6
7#include "audio_core/adsp/apps/audio_renderer/audio_renderer.h"
8#include "audio_core/adsp/apps/audio_renderer/command_buffer.h"
7#include "audio_core/audio_core.h" 9#include "audio_core/audio_core.h"
8#include "audio_core/common/audio_renderer_parameter.h" 10#include "audio_core/common/audio_renderer_parameter.h"
9#include "audio_core/common/common.h" 11#include "audio_core/common/common.h"
10#include "audio_core/common/feature_support.h" 12#include "audio_core/common/feature_support.h"
11#include "audio_core/common/workbuffer_allocator.h" 13#include "audio_core/common/workbuffer_allocator.h"
12#include "audio_core/renderer/adsp/adsp.h"
13#include "audio_core/renderer/behavior/info_updater.h" 14#include "audio_core/renderer/behavior/info_updater.h"
14#include "audio_core/renderer/command/command_buffer.h" 15#include "audio_core/renderer/command/command_buffer.h"
15#include "audio_core/renderer/command/command_generator.h" 16#include "audio_core/renderer/command/command_generator.h"
@@ -34,7 +35,7 @@
34#include "core/hle/kernel/k_transfer_memory.h" 35#include "core/hle/kernel/k_transfer_memory.h"
35#include "core/memory.h" 36#include "core/memory.h"
36 37
37namespace AudioCore::AudioRenderer { 38namespace AudioCore::Renderer {
38 39
39u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) { 40u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
40 BehaviorInfo behavior; 41 BehaviorInfo behavior;
@@ -95,7 +96,8 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
95} 96}
96 97
97System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_) 98System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
98 : core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {} 99 : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
100 adsp_rendered_event{adsp_rendered_event_} {}
99 101
100Result System::Initialize(const AudioRendererParameterInternal& params, 102Result System::Initialize(const AudioRendererParameterInternal& params,
101 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, 103 Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
@@ -443,7 +445,7 @@ void System::Stop() {
443Result System::Update(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) { 445Result System::Update(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) {
444 std::scoped_lock l{lock}; 446 std::scoped_lock l{lock};
445 447
446 const auto start_time{core.CoreTiming().GetClockTicks()}; 448 const auto start_time{core.CoreTiming().GetGlobalTimeNs().count()};
447 std::memset(output.data(), 0, output.size()); 449 std::memset(output.data(), 0, output.size());
448 450
449 InfoUpdater info_updater(input, output, process_handle, behavior); 451 InfoUpdater info_updater(input, output, process_handle, behavior);
@@ -535,7 +537,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
535 adsp_rendered_event->Clear(); 537 adsp_rendered_event->Clear();
536 num_times_updated++; 538 num_times_updated++;
537 539
538 const auto end_time{core.CoreTiming().GetClockTicks()}; 540 const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
539 ticks_spent_updating += end_time - start_time; 541 ticks_spent_updating += end_time - start_time;
540 542
541 return ResultSuccess; 543 return ResultSuccess;
@@ -583,7 +585,7 @@ void System::SendCommandToDsp() {
583 if (initialized) { 585 if (initialized) {
584 if (active) { 586 if (active) {
585 terminate_event.Reset(); 587 terminate_event.Reset();
586 const auto remaining_command_count{adsp.GetRemainCommandCount(session_id)}; 588 const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
587 u64 command_size{0}; 589 u64 command_size{0};
588 590
589 if (remaining_command_count) { 591 if (remaining_command_count) {
@@ -607,26 +609,18 @@ void System::SendCommandToDsp() {
607 time_limit_percent = 70.0f; 609 time_limit_percent = 70.0f;
608 } 610 }
609 611
610 ADSP::CommandBuffer command_buffer{ 612 auto time_limit{
611 .buffer{translated_addr}, 613 static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 *
612 .size{command_size}, 614 (static_cast<f32>(render_time_limit_percent) / 100.0f))};
613 .time_limit{ 615 audio_renderer.SetCommandBuffer(session_id, translated_addr, command_size, time_limit,
614 static_cast<u64>((time_limit_percent / 100) * 2'880'000.0 * 616 applet_resource_user_id, reset_command_buffers);
615 (static_cast<f32>(render_time_limit_percent) / 100.0f))},
616 .remaining_command_count{remaining_command_count},
617 .reset_buffers{reset_command_buffers},
618 .applet_resource_user_id{applet_resource_user_id},
619 .render_time_taken{adsp.GetRenderTimeTaken(session_id)},
620 };
621
622 adsp.SendCommandBuffer(session_id, command_buffer);
623 reset_command_buffers = false; 617 reset_command_buffers = false;
624 command_buffer_size = command_size; 618 command_buffer_size = command_size;
625 if (remaining_command_count == 0) { 619 if (remaining_command_count == 0) {
626 adsp_rendered_event->Signal(); 620 adsp_rendered_event->Signal();
627 } 621 }
628 } else { 622 } else {
629 adsp.ClearRemainCount(session_id); 623 audio_renderer.ClearRemainCommandCount(session_id);
630 terminate_event.Set(); 624 terminate_event.Set();
631 } 625 }
632 } 626 }
@@ -635,7 +629,7 @@ void System::SendCommandToDsp() {
635u64 System::GenerateCommand(std::span<u8> in_command_buffer, 629u64 System::GenerateCommand(std::span<u8> in_command_buffer,
636 [[maybe_unused]] u64 command_buffer_size_) { 630 [[maybe_unused]] u64 command_buffer_size_) {
637 PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count); 631 PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count);
638 const auto start_time{core.CoreTiming().GetClockTicks()}; 632 const auto start_time{core.CoreTiming().GetGlobalTimeNs().count()};
639 633
640 auto command_list_header{reinterpret_cast<CommandListHeader*>(in_command_buffer.data())}; 634 auto command_list_header{reinterpret_cast<CommandListHeader*>(in_command_buffer.data())};
641 635
@@ -732,10 +726,10 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
732 effect_context.UpdateStateByDspShared(); 726 effect_context.UpdateStateByDspShared();
733 } 727 }
734 728
735 const auto end_time{core.CoreTiming().GetClockTicks()}; 729 const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
736 total_ticks_elapsed += end_time - start_time; 730 total_ticks_elapsed += end_time - start_time;
737 num_command_lists_generated++; 731 num_command_lists_generated++;
738 render_start_tick = adsp.GetRenderingStartTick(session_id); 732 render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
739 frames_elapsed++; 733 frames_elapsed++;
740 734
741 return command_buffer.size; 735 return command_buffer.size;
@@ -778,7 +772,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time
778 while (i < command_buffer.count) { 772 while (i < command_buffer.count) {
779 const auto node_id{cmd->node_id}; 773 const auto node_id{cmd->node_id};
780 const auto node_id_type{cmd->node_id >> 28}; 774 const auto node_id_type{cmd->node_id >> 28};
781 const auto node_id_base{cmd->node_id & 0xFFF}; 775 const auto node_id_base{(cmd->node_id >> 16) & 0xFFF};
782 776
783 // If the new estimated process time falls below the limit, we're done dropping. 777 // If the new estimated process time falls below the limit, we're done dropping.
784 if (estimated_process_time <= time_limit) { 778 if (estimated_process_time <= time_limit) {
@@ -819,4 +813,4 @@ u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time
819 return voices_dropped; 813 return voices_dropped;
820} 814}
821 815
822} // namespace AudioCore::AudioRenderer 816} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h
index e328783b6..8a8341710 100644
--- a/src/audio_core/renderer/system.h
+++ b/src/audio_core/renderer/system.h
@@ -34,12 +34,16 @@ class KTransferMemory;
34 34
35namespace AudioCore { 35namespace AudioCore {
36struct AudioRendererParameterInternal; 36struct AudioRendererParameterInternal;
37
38namespace AudioRenderer {
39class CommandBuffer;
40namespace ADSP { 37namespace ADSP {
41class ADSP; 38class ADSP;
39namespace AudioRenderer {
40class AudioRenderer;
42} 41}
42} // namespace ADSP
43
44namespace Renderer {
45using namespace ::AudioCore::ADSP;
46class CommandBuffer;
43 47
44/** 48/**
45 * Audio Renderer System, the main worker for audio rendering. 49 * Audio Renderer System, the main worker for audio rendering.
@@ -213,8 +217,8 @@ public:
213private: 217private:
214 /// Core system 218 /// Core system
215 Core::System& core; 219 Core::System& core;
216 /// Reference to the ADSP for communication 220 /// Reference to the ADSP's AudioRenderer for communication
217 ADSP::ADSP& adsp; 221 ::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
218 /// Is this system initialized? 222 /// Is this system initialized?
219 bool initialized{}; 223 bool initialized{};
220 /// Is this system currently active? 224 /// Is this system currently active?
@@ -319,5 +323,5 @@ private:
319 f32 drop_voice_param{1.0f}; 323 f32 drop_voice_param{1.0f};
320}; 324};
321 325
322} // namespace AudioRenderer 326} // namespace Renderer
323} // namespace AudioCore 327} // namespace AudioCore
diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp
index 300ecdbf1..a0b8ef29e 100644
--- a/src/audio_core/renderer/system_manager.cpp
+++ b/src/audio_core/renderer/system_manager.cpp
@@ -3,8 +3,8 @@
3 3
4#include <chrono> 4#include <chrono>
5 5
6#include "audio_core/adsp/adsp.h"
6#include "audio_core/audio_core.h" 7#include "audio_core/audio_core.h"
7#include "audio_core/renderer/adsp/adsp.h"
8#include "audio_core/renderer/system_manager.h" 8#include "audio_core/renderer/system_manager.h"
9#include "common/microprofile.h" 9#include "common/microprofile.h"
10#include "common/thread.h" 10#include "common/thread.h"
@@ -14,24 +14,21 @@
14MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager", 14MICROPROFILE_DEFINE(Audio_RenderSystemManager, "Audio", "Render System Manager",
15 MP_RGB(60, 19, 97)); 15 MP_RGB(60, 19, 97));
16 16
17namespace AudioCore::AudioRenderer { 17namespace AudioCore::Renderer {
18 18
19SystemManager::SystemManager(Core::System& core_) 19SystemManager::SystemManager(Core::System& core_)
20 : core{core_}, adsp{core.AudioCore().GetADSP()}, mailbox{adsp.GetRenderMailbox()} {} 20 : core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
21 21
22SystemManager::~SystemManager() { 22SystemManager::~SystemManager() {
23 Stop(); 23 Stop();
24} 24}
25 25
26bool SystemManager::InitializeUnsafe() { 26void SystemManager::InitializeUnsafe() {
27 if (!active) { 27 if (!active) {
28 if (adsp.Start()) { 28 active = true;
29 active = true; 29 audio_renderer.Start();
30 thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); }); 30 thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); });
31 }
32 } 31 }
33
34 return adsp.GetState() == ADSP::State::Started;
35} 32}
36 33
37void SystemManager::Stop() { 34void SystemManager::Stop() {
@@ -41,7 +38,7 @@ void SystemManager::Stop() {
41 active = false; 38 active = false;
42 thread.request_stop(); 39 thread.request_stop();
43 thread.join(); 40 thread.join();
44 adsp.Stop(); 41 audio_renderer.Stop();
45} 42}
46 43
47bool SystemManager::Add(System& system_) { 44bool SystemManager::Add(System& system_) {
@@ -55,10 +52,7 @@ bool SystemManager::Add(System& system_) {
55 { 52 {
56 std::scoped_lock l{mutex1}; 53 std::scoped_lock l{mutex1};
57 if (systems.empty()) { 54 if (systems.empty()) {
58 if (!InitializeUnsafe()) { 55 InitializeUnsafe();
59 LOG_ERROR(Service_Audio, "Failed to start the AudioRenderer SystemManager");
60 return false;
61 }
62 } 56 }
63 } 57 }
64 58
@@ -100,9 +94,9 @@ void SystemManager::ThreadFunc(std::stop_token stop_token) {
100 } 94 }
101 } 95 }
102 96
103 adsp.Signal(); 97 audio_renderer.Signal();
104 adsp.Wait(); 98 audio_renderer.Wait();
105 } 99 }
106} 100}
107 101
108} // namespace AudioCore::AudioRenderer 102} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/system_manager.h b/src/audio_core/renderer/system_manager.h
index 9681fd121..62e8e5f15 100644
--- a/src/audio_core/renderer/system_manager.h
+++ b/src/audio_core/renderer/system_manager.h
@@ -18,11 +18,14 @@ struct EventType;
18class System; 18class System;
19} // namespace Core 19} // namespace Core
20 20
21namespace AudioCore::AudioRenderer { 21namespace AudioCore::ADSP {
22namespace ADSP {
23class ADSP; 22class ADSP;
24class AudioRenderer_Mailbox; 23namespace AudioRenderer {
25} // namespace ADSP 24class AudioRenderer;
25} // namespace AudioRenderer
26} // namespace AudioCore::ADSP
27
28namespace AudioCore::Renderer {
26 29
27/** 30/**
28 * Manages all audio renderers, responsible for triggering command list generation and signalling 31 * Manages all audio renderers, responsible for triggering command list generation and signalling
@@ -38,7 +41,7 @@ public:
38 * 41 *
39 * @return True if successfully initialized, otherwise false. 42 * @return True if successfully initialized, otherwise false.
40 */ 43 */
41 bool InitializeUnsafe(); 44 void InitializeUnsafe();
42 45
43 /** 46 /**
44 * Stop the system manager. 47 * Stop the system manager.
@@ -80,10 +83,8 @@ private:
80 std::mutex mutex2{}; 83 std::mutex mutex2{};
81 /// Is the system manager thread active? 84 /// Is the system manager thread active?
82 std::atomic<bool> active{}; 85 std::atomic<bool> active{};
83 /// Reference to the ADSP for communication 86 /// Reference to the ADSP's AudioRenderer for communication
84 ADSP::ADSP& adsp; 87 ::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
85 /// AudioRenderer mailbox for communication
86 ADSP::AudioRenderer_Mailbox* mailbox{};
87}; 88};
88 89
89} // namespace AudioCore::AudioRenderer 90} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/upsampler/upsampler_info.h b/src/audio_core/renderer/upsampler/upsampler_info.h
index a43c15af3..85c87f137 100644
--- a/src/audio_core/renderer/upsampler/upsampler_info.h
+++ b/src/audio_core/renderer/upsampler/upsampler_info.h
@@ -9,7 +9,7 @@
9#include "audio_core/renderer/upsampler/upsampler_state.h" 9#include "audio_core/renderer/upsampler/upsampler_state.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13class UpsamplerManager; 13class UpsamplerManager;
14 14
15/** 15/**
@@ -32,4 +32,4 @@ struct UpsamplerInfo {
32 std::array<s16, MaxChannels> inputs{}; 32 std::array<s16, MaxChannels> inputs{};
33}; 33};
34 34
35} // namespace AudioCore::AudioRenderer 35} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/upsampler/upsampler_manager.cpp b/src/audio_core/renderer/upsampler/upsampler_manager.cpp
index 4c76a5066..ef740f6c9 100644
--- a/src/audio_core/renderer/upsampler/upsampler_manager.cpp
+++ b/src/audio_core/renderer/upsampler/upsampler_manager.cpp
@@ -3,7 +3,7 @@
3 3
4#include "audio_core/renderer/upsampler/upsampler_manager.h" 4#include "audio_core/renderer/upsampler/upsampler_manager.h"
5 5
6namespace AudioCore::AudioRenderer { 6namespace AudioCore::Renderer {
7 7
8UpsamplerManager::UpsamplerManager(const u32 count_, std::span<UpsamplerInfo> infos_, 8UpsamplerManager::UpsamplerManager(const u32 count_, std::span<UpsamplerInfo> infos_,
9 std::span<s32> workbuffer_) 9 std::span<s32> workbuffer_)
@@ -41,4 +41,4 @@ void UpsamplerManager::Free(UpsamplerInfo* info) {
41 info->enabled = false; 41 info->enabled = false;
42} 42}
43 43
44} // namespace AudioCore::AudioRenderer 44} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/upsampler/upsampler_manager.h b/src/audio_core/renderer/upsampler/upsampler_manager.h
index 83c697c0c..263e5718b 100644
--- a/src/audio_core/renderer/upsampler/upsampler_manager.h
+++ b/src/audio_core/renderer/upsampler/upsampler_manager.h
@@ -9,7 +9,7 @@
9#include "audio_core/renderer/upsampler/upsampler_info.h" 9#include "audio_core/renderer/upsampler/upsampler_info.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13/** 13/**
14 * Manages and has utility functions for upsampler infos. 14 * Manages and has utility functions for upsampler infos.
15 */ 15 */
@@ -42,4 +42,4 @@ private:
42 std::mutex lock{}; 42 std::mutex lock{};
43}; 43};
44 44
45} // namespace AudioCore::AudioRenderer 45} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/upsampler/upsampler_state.h b/src/audio_core/renderer/upsampler/upsampler_state.h
index 28cebe200..dc7b31d42 100644
--- a/src/audio_core/renderer/upsampler/upsampler_state.h
+++ b/src/audio_core/renderer/upsampler/upsampler_state.h
@@ -8,7 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/fixed_point.h" 9#include "common/fixed_point.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::Renderer {
12/** 12/**
13 * Upsampling state used by the AudioRenderer across calls. 13 * Upsampling state used by the AudioRenderer across calls.
14 */ 14 */
@@ -37,4 +37,4 @@ struct UpsamplerState {
37 u8 sample_index; 37 u8 sample_index;
38}; 38};
39 39
40} // namespace AudioCore::AudioRenderer 40} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/voice/voice_channel_resource.h b/src/audio_core/renderer/voice/voice_channel_resource.h
index 26ab4ccce..4f19c2fcc 100644
--- a/src/audio_core/renderer/voice/voice_channel_resource.h
+++ b/src/audio_core/renderer/voice/voice_channel_resource.h
@@ -8,7 +8,7 @@
8#include "audio_core/common/common.h" 8#include "audio_core/common/common.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10 10
11namespace AudioCore::AudioRenderer { 11namespace AudioCore::Renderer {
12/** 12/**
13 * Represents one channel for mixing a voice. 13 * Represents one channel for mixing a voice.
14 */ 14 */
@@ -35,4 +35,4 @@ public:
35 bool in_use{}; 35 bool in_use{};
36}; 36};
37 37
38} // namespace AudioCore::AudioRenderer 38} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp
index 16a3e839d..c3644e38b 100644
--- a/src/audio_core/renderer/voice/voice_context.cpp
+++ b/src/audio_core/renderer/voice/voice_context.cpp
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/voice/voice_context.h" 6#include "audio_core/renderer/voice/voice_context.h"
7#include "common/polyfill_ranges.h" 7#include "common/polyfill_ranges.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11VoiceState& VoiceContext::GetDspSharedState(const u32 index) { 11VoiceState& VoiceContext::GetDspSharedState(const u32 index) {
12 if (index >= dsp_states.size()) { 12 if (index >= dsp_states.size()) {
@@ -84,4 +84,4 @@ void VoiceContext::UpdateStateByDspShared() {
84 std::memcpy(cpu_states.data(), dsp_states.data(), voice_count * sizeof(VoiceState)); 84 std::memcpy(cpu_states.data(), dsp_states.data(), voice_count * sizeof(VoiceState));
85} 85}
86 86
87} // namespace AudioCore::AudioRenderer 87} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/voice/voice_context.h b/src/audio_core/renderer/voice/voice_context.h
index 43b677154..138ab2773 100644
--- a/src/audio_core/renderer/voice/voice_context.h
+++ b/src/audio_core/renderer/voice/voice_context.h
@@ -10,7 +10,7 @@
10#include "audio_core/renderer/voice/voice_state.h" 10#include "audio_core/renderer/voice/voice_state.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace AudioCore::AudioRenderer { 13namespace AudioCore::Renderer {
14/** 14/**
15 * Contains all voices, with utility functions for managing them. 15 * Contains all voices, with utility functions for managing them.
16 */ 16 */
@@ -123,4 +123,4 @@ private:
123 u32 active_count{}; 123 u32 active_count{};
124}; 124};
125 125
126} // namespace AudioCore::AudioRenderer 126} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/voice/voice_info.cpp b/src/audio_core/renderer/voice/voice_info.cpp
index c0bfb23fc..6239cfab7 100644
--- a/src/audio_core/renderer/voice/voice_info.cpp
+++ b/src/audio_core/renderer/voice/voice_info.cpp
@@ -6,7 +6,7 @@
6#include "audio_core/renderer/voice/voice_info.h" 6#include "audio_core/renderer/voice/voice_info.h"
7#include "audio_core/renderer/voice/voice_state.h" 7#include "audio_core/renderer/voice/voice_state.h"
8 8
9namespace AudioCore::AudioRenderer { 9namespace AudioCore::Renderer {
10 10
11VoiceInfo::VoiceInfo() { 11VoiceInfo::VoiceInfo() {
12 Initialize(); 12 Initialize();
@@ -405,4 +405,4 @@ void VoiceInfo::ResetResources(VoiceContext& voice_context) const {
405 } 405 }
406} 406}
407 407
408} // namespace AudioCore::AudioRenderer 408} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/voice/voice_info.h b/src/audio_core/renderer/voice/voice_info.h
index 3c5d3e04f..14a687dcb 100644
--- a/src/audio_core/renderer/voice/voice_info.h
+++ b/src/audio_core/renderer/voice/voice_info.h
@@ -12,7 +12,7 @@
12#include "audio_core/renderer/memory/address_info.h" 12#include "audio_core/renderer/memory/address_info.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14 14
15namespace AudioCore::AudioRenderer { 15namespace AudioCore::Renderer {
16class PoolMapper; 16class PoolMapper;
17class VoiceContext; 17class VoiceContext;
18struct VoiceState; 18struct VoiceState;
@@ -377,4 +377,4 @@ public:
377 u8 flush_buffer_count{}; 377 u8 flush_buffer_count{};
378}; 378};
379 379
380} // namespace AudioCore::AudioRenderer 380} // namespace AudioCore::Renderer
diff --git a/src/audio_core/renderer/voice/voice_state.h b/src/audio_core/renderer/voice/voice_state.h
index ce947233f..c7aee167b 100644
--- a/src/audio_core/renderer/voice/voice_state.h
+++ b/src/audio_core/renderer/voice/voice_state.h
@@ -9,7 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/fixed_point.h" 10#include "common/fixed_point.h"
11 11
12namespace AudioCore::AudioRenderer { 12namespace AudioCore::Renderer {
13/** 13/**
14 * Holds a state for a voice. One is kept host-side, and one is used by the AudioRenderer, 14 * Holds a state for a voice. One is kept host-side, and one is used by the AudioRenderer,
15 * host-side is updated on the next iteration. 15 * host-side is updated on the next iteration.
@@ -67,4 +67,4 @@ struct VoiceState {
67 s32 loop_count; 67 s32 loop_count;
68}; 68};
69 69
70} // namespace AudioCore::AudioRenderer 70} // namespace AudioCore::Renderer
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index 9a0801888..bbb598bc5 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -8,6 +8,7 @@
8#include "audio_core/sink/cubeb_sink.h" 8#include "audio_core/sink/cubeb_sink.h"
9#include "audio_core/sink/sink_stream.h" 9#include "audio_core/sink/sink_stream.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/scope_exit.h"
11#include "core/core.h" 12#include "core/core.h"
12 13
13#ifdef _WIN32 14#ifdef _WIN32
@@ -332,25 +333,38 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
332 return device_list; 333 return device_list;
333} 334}
334 335
335u32 GetCubebLatency() { 336namespace {
336 cubeb* ctx; 337static long TmpDataCallback(cubeb_stream*, void*, const void*, void*, long) {
338 return TargetSampleCount;
339}
340static void TmpStateCallback(cubeb_stream*, void*, cubeb_state) {}
341} // namespace
342
343bool IsCubebSuitable() {
344#if !defined(HAVE_CUBEB)
345 return false;
346#else
347 cubeb* ctx{nullptr};
337 348
338#ifdef _WIN32 349#ifdef _WIN32
339 auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); 350 auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
340#endif 351#endif
341 352
353 // Init cubeb
342 if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { 354 if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
343 LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); 355 LOG_ERROR(Audio_Sink, "Cubeb failed to init, it is not suitable.");
344 // Return a large latency so we choose SDL instead. 356 return false;
345 return 10000u;
346 } 357 }
347 358
359 SCOPE_EXIT({ cubeb_destroy(ctx); });
360
348#ifdef _WIN32 361#ifdef _WIN32
349 if (SUCCEEDED(com_init_result)) { 362 if (SUCCEEDED(com_init_result)) {
350 CoUninitialize(); 363 CoUninitialize();
351 } 364 }
352#endif 365#endif
353 366
367 // Get min latency
354 cubeb_stream_params params{}; 368 cubeb_stream_params params{};
355 params.rate = TargetSampleRate; 369 params.rate = TargetSampleRate;
356 params.channels = 2; 370 params.channels = 2;
@@ -361,12 +375,27 @@ u32 GetCubebLatency() {
361 u32 latency{0}; 375 u32 latency{0};
362 const auto latency_error = cubeb_get_min_latency(ctx, &params, &latency); 376 const auto latency_error = cubeb_get_min_latency(ctx, &params, &latency);
363 if (latency_error != CUBEB_OK) { 377 if (latency_error != CUBEB_OK) {
364 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); 378 LOG_ERROR(Audio_Sink, "Cubeb could not get min latency, it is not suitable.");
365 latency = TargetSampleCount * 2; 379 return false;
366 } 380 }
367 latency = std::max(latency, TargetSampleCount * 2); 381 latency = std::max(latency, TargetSampleCount * 2);
368 cubeb_destroy(ctx); 382
369 return latency; 383 // Test opening a device with standard parameters
384 cubeb_devid output_device{0};
385 cubeb_devid input_device{0};
386 std::string name{"Yuzu test"};
387 cubeb_stream* stream{nullptr};
388
389 if (cubeb_stream_init(ctx, &stream, name.c_str(), input_device, nullptr, output_device, &params,
390 latency, &TmpDataCallback, &TmpStateCallback, nullptr) != CUBEB_OK) {
391 LOG_CRITICAL(Audio_Sink, "Cubeb could not open a device, it is not suitable.");
392 return false;
393 }
394
395 cubeb_stream_stop(stream);
396 cubeb_stream_destroy(stream);
397 return true;
398#endif
370} 399}
371 400
372} // namespace AudioCore::Sink 401} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h
index 3302cb98d..f49a6fdaa 100644
--- a/src/audio_core/sink/cubeb_sink.h
+++ b/src/audio_core/sink/cubeb_sink.h
@@ -97,10 +97,11 @@ private:
97std::vector<std::string> ListCubebSinkDevices(bool capture); 97std::vector<std::string> ListCubebSinkDevices(bool capture);
98 98
99/** 99/**
100 * Get the reported latency for this sink. 100 * Check if this backend is suitable for use.
101 * Checks if enabled, its latency, whether it opens successfully, etc.
101 * 102 *
102 * @return Minimum latency for this sink. 103 * @return True is this backend is suitable, false otherwise.
103 */ 104 */
104u32 GetCubebLatency(); 105bool IsCubebSuitable();
105 106
106} // namespace AudioCore::Sink 107} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index c1529d1f9..7b89151de 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -9,6 +9,7 @@
9#include "audio_core/sink/sdl2_sink.h" 9#include "audio_core/sink/sdl2_sink.h"
10#include "audio_core/sink/sink_stream.h" 10#include "audio_core/sink/sink_stream.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/scope_exit.h"
12#include "core/core.h" 13#include "core/core.h"
13 14
14namespace AudioCore::Sink { 15namespace AudioCore::Sink {
@@ -84,6 +85,7 @@ public:
84 } 85 }
85 86
86 Stop(); 87 Stop();
88 SDL_ClearQueuedAudio(device);
87 SDL_CloseAudioDevice(device); 89 SDL_CloseAudioDevice(device);
88 } 90 }
89 91
@@ -227,8 +229,42 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
227 return device_list; 229 return device_list;
228} 230}
229 231
230u32 GetSDLLatency() { 232bool IsSDLSuitable() {
231 return TargetSampleCount * 2; 233#if !defined(HAVE_SDL2)
234 return false;
235#else
236 // Check SDL can init
237 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
238 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
239 LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
240 SDL_GetError());
241 return false;
242 }
243 }
244
245 // We can set any latency frequency we want with SDL, so no need to check that.
246
247 // Check we can open a device with standard parameters
248 SDL_AudioSpec spec;
249 spec.freq = TargetSampleRate;
250 spec.channels = 2u;
251 spec.format = AUDIO_S16SYS;
252 spec.samples = TargetSampleCount * 2;
253 spec.callback = nullptr;
254 spec.userdata = nullptr;
255
256 SDL_AudioSpec obtained;
257 auto device = SDL_OpenAudioDevice(nullptr, false, &spec, &obtained, false);
258
259 if (device == 0) {
260 LOG_ERROR(Audio_Sink, "SDL failed to open a device, it is not suitable. Error: {}",
261 SDL_GetError());
262 return false;
263 }
264
265 SDL_CloseAudioDevice(device);
266 return true;
267#endif
232} 268}
233 269
234} // namespace AudioCore::Sink 270} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h
index 27ed1ab94..9211d2e97 100644
--- a/src/audio_core/sink/sdl2_sink.h
+++ b/src/audio_core/sink/sdl2_sink.h
@@ -88,10 +88,11 @@ private:
88std::vector<std::string> ListSDLSinkDevices(bool capture); 88std::vector<std::string> ListSDLSinkDevices(bool capture);
89 89
90/** 90/**
91 * Get the reported latency for this sink. 91 * Check if this backend is suitable for use.
92 * Checks if enabled, its latency, whether it opens successfully, etc.
92 * 93 *
93 * @return Minimum latency for this sink. 94 * @return True is this backend is suitable, false otherwise.
94 */ 95 */
95u32 GetSDLLatency(); 96bool IsSDLSuitable();
96 97
97} // namespace AudioCore::Sink 98} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 027bfa517..7c9a4e3ac 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -22,7 +22,7 @@ namespace {
22struct SinkDetails { 22struct SinkDetails {
23 using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); 23 using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
24 using ListDevicesFn = std::vector<std::string> (*)(bool); 24 using ListDevicesFn = std::vector<std::string> (*)(bool);
25 using LatencyFn = u32 (*)(); 25 using SuitableFn = bool (*)();
26 26
27 /// Name for this sink. 27 /// Name for this sink.
28 Settings::AudioEngine id; 28 Settings::AudioEngine id;
@@ -30,8 +30,8 @@ struct SinkDetails {
30 FactoryFn factory; 30 FactoryFn factory;
31 /// A method to call to list available devices. 31 /// A method to call to list available devices.
32 ListDevicesFn list_devices; 32 ListDevicesFn list_devices;
33 /// Method to get the latency of this backend. 33 /// Check whether this backend is suitable to be used.
34 LatencyFn latency; 34 SuitableFn is_suitable;
35}; 35};
36 36
37// sink_details is ordered in terms of desirability, with the best choice at the top. 37// sink_details is ordered in terms of desirability, with the best choice at the top.
@@ -43,7 +43,7 @@ constexpr SinkDetails sink_details[] = {
43 return std::make_unique<CubebSink>(device_id); 43 return std::make_unique<CubebSink>(device_id);
44 }, 44 },
45 &ListCubebSinkDevices, 45 &ListCubebSinkDevices,
46 &GetCubebLatency, 46 &IsCubebSuitable,
47 }, 47 },
48#endif 48#endif
49#ifdef HAVE_SDL2 49#ifdef HAVE_SDL2
@@ -53,14 +53,17 @@ constexpr SinkDetails sink_details[] = {
53 return std::make_unique<SDLSink>(device_id); 53 return std::make_unique<SDLSink>(device_id);
54 }, 54 },
55 &ListSDLSinkDevices, 55 &ListSDLSinkDevices,
56 &GetSDLLatency, 56 &IsSDLSuitable,
57 }, 57 },
58#endif 58#endif
59 SinkDetails{Settings::AudioEngine::Null, 59 SinkDetails{
60 [](std::string_view device_id) -> std::unique_ptr<Sink> { 60 Settings::AudioEngine::Null,
61 return std::make_unique<NullSink>(device_id); 61 [](std::string_view device_id) -> std::unique_ptr<Sink> {
62 }, 62 return std::make_unique<NullSink>(device_id);
63 [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, 63 },
64 [](bool capture) { return std::vector<std::string>{"null"}; },
65 []() { return true; },
66 },
64}; 67};
65 68
66const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) { 69const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
@@ -72,18 +75,22 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
72 auto iter = find_backend(sink_id); 75 auto iter = find_backend(sink_id);
73 76
74 if (sink_id == Settings::AudioEngine::Auto) { 77 if (sink_id == Settings::AudioEngine::Auto) {
75 // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which 78 // Auto-select a backend. Use the sink details ordering, preferring cubeb first, checking
76 // causes audio issues, in that case go with SDL. 79 // that the backend is available and suitable to use.
77#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) 80 for (auto& details : sink_details) {
78 iter = find_backend(Settings::AudioEngine::Cubeb); 81 if (details.is_suitable()) {
79 if (iter->latency() > TargetSampleCount * 3) { 82 iter = &details;
80 iter = find_backend(Settings::AudioEngine::Sdl2); 83 break;
84 }
81 } 85 }
82#else
83 iter = std::begin(sink_details);
84#endif
85 LOG_INFO(Service_Audio, "Auto-selecting the {} backend", 86 LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
86 Settings::CanonicalizeEnum(iter->id)); 87 Settings::CanonicalizeEnum(iter->id));
88 } else {
89 if (iter != std::end(sink_details) && !iter->is_suitable()) {
90 LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
91 Settings::CanonicalizeEnum(iter->id));
92 iter = find_backend(Settings::AudioEngine::Null);
93 }
87 } 94 }
88 95
89 if (iter == std::end(sink_details)) { 96 if (iter == std::end(sink_details)) {
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index bf97d9ba2..34877b461 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -151,6 +151,10 @@ add_library(common STATIC
151 zstd_compression.h 151 zstd_compression.h
152) 152)
153 153
154if (YUZU_ENABLE_PORTABLE)
155 add_compile_definitions(YUZU_ENABLE_PORTABLE)
156endif()
157
154if (WIN32) 158if (WIN32)
155 target_sources(common PRIVATE 159 target_sources(common PRIVATE
156 windows/timer_resolution.cpp 160 windows/timer_resolution.cpp
@@ -191,8 +195,6 @@ if (MSVC)
191 _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 195 _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
192 ) 196 )
193 target_compile_options(common PRIVATE 197 target_compile_options(common PRIVATE
194 /W4
195
196 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 198 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
197 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 199 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
198 /we4800 # Implicit conversion from 'type' to bool. Possible information loss 200 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index d71cfacc6..dce219fcf 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -88,8 +88,9 @@ public:
88 fs::path yuzu_path_config; 88 fs::path yuzu_path_config;
89 89
90#ifdef _WIN32 90#ifdef _WIN32
91#ifdef YUZU_ENABLE_PORTABLE
91 yuzu_path = GetExeDirectory() / PORTABLE_DIR; 92 yuzu_path = GetExeDirectory() / PORTABLE_DIR;
92 93#endif
93 if (!IsDir(yuzu_path)) { 94 if (!IsDir(yuzu_path)) {
94 yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR; 95 yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR;
95 } 96 }
@@ -101,8 +102,9 @@ public:
101 yuzu_path_cache = yuzu_path / CACHE_DIR; 102 yuzu_path_cache = yuzu_path / CACHE_DIR;
102 yuzu_path_config = yuzu_path / CONFIG_DIR; 103 yuzu_path_config = yuzu_path / CONFIG_DIR;
103#else 104#else
105#ifdef YUZU_ENABLE_PORTABLE
104 yuzu_path = GetCurrentDir() / PORTABLE_DIR; 106 yuzu_path = GetCurrentDir() / PORTABLE_DIR;
105 107#endif
106 if (Exists(yuzu_path) && IsDir(yuzu_path)) { 108 if (Exists(yuzu_path) && IsDir(yuzu_path)) {
107 yuzu_path_cache = yuzu_path / CACHE_DIR; 109 yuzu_path_cache = yuzu_path / CACHE_DIR;
108 yuzu_path_config = yuzu_path / CONFIG_DIR; 110 yuzu_path_config = yuzu_path / CONFIG_DIR;
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 16a58a750..4ecaf550b 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <version> 4#include <version>
5#include "common/settings_enums.h"
5#if __cpp_lib_chrono >= 201907L 6#if __cpp_lib_chrono >= 201907L
6#include <chrono> 7#include <chrono>
7#include <exception> 8#include <exception>
@@ -145,6 +146,10 @@ bool IsFastmemEnabled() {
145 return true; 146 return true;
146} 147}
147 148
149bool IsDockedMode() {
150 return values.use_docked_mode.GetValue() == Settings::ConsoleMode::Docked;
151}
152
148float Volume() { 153float Volume() {
149 if (values.audio_muted) { 154 if (values.audio_muted) {
150 return 0.0f; 155 return 0.0f;
@@ -154,6 +159,8 @@ float Volume() {
154 159
155const char* TranslateCategory(Category category) { 160const char* TranslateCategory(Category category) {
156 switch (category) { 161 switch (category) {
162 case Category::Android:
163 return "Android";
157 case Category::Audio: 164 case Category::Audio:
158 return "Audio"; 165 return "Audio";
159 case Category::Core: 166 case Category::Core:
diff --git a/src/common/settings.h b/src/common/settings.h
index 4407c1e6d..b15213bd7 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -379,7 +379,13 @@ struct Values {
379 379
380 Setting<s32> current_user{linkage, 0, "current_user", Category::System}; 380 Setting<s32> current_user{linkage, 0, "current_user", Category::System};
381 381
382 SwitchableSetting<bool> use_docked_mode{linkage, true, "use_docked_mode", Category::System}; 382 SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
383 ConsoleMode::Docked,
384 "use_docked_mode",
385 Category::System,
386 Specialization::Radio,
387 true,
388 true};
383 389
384 // Controls 390 // Controls
385 InputSetting<std::array<PlayerInput, 10>> players; 391 InputSetting<std::array<PlayerInput, 10>> players;
@@ -519,6 +525,8 @@ bool IsGPULevelHigh();
519 525
520bool IsFastmemEnabled(); 526bool IsFastmemEnabled();
521 527
528bool IsDockedMode();
529
522float Volume(); 530float Volume();
523 531
524std::string GetTimeZoneString(TimeZone time_zone); 532std::string GetTimeZoneString(TimeZone time_zone);
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
index 137b65d5f..5960b78aa 100644
--- a/src/common/settings_common.cpp
+++ b/src/common/settings_common.cpp
@@ -14,6 +14,7 @@ BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Categ
14 : label{name}, category{category_}, id{linkage.count}, save{save_}, 14 : label{name}, category{category_}, id{linkage.count}, save{save_},
15 runtime_modifiable{runtime_modifiable_}, specialization{specialization_}, 15 runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
16 other_setting{other_setting_} { 16 other_setting{other_setting_} {
17 linkage.by_key.insert({name, this});
17 linkage.by_category[category].push_back(this); 18 linkage.by_category[category].push_back(this);
18 linkage.count++; 19 linkage.count++;
19} 20}
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 2efb329b0..5b170dfd5 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -12,6 +12,7 @@
12namespace Settings { 12namespace Settings {
13 13
14enum class Category : u32 { 14enum class Category : u32 {
15 Android,
15 Audio, 16 Audio,
16 Core, 17 Core,
17 Cpu, 18 Cpu,
@@ -56,6 +57,7 @@ enum Specialization : u8 {
56 Scalar = 5, // Values are continuous 57 Scalar = 5, // Values are continuous
57 Countable = 6, // Can be stepped through 58 Countable = 6, // Can be stepped through
58 Paired = 7, // Another setting is associated with this setting 59 Paired = 7, // Another setting is associated with this setting
60 Radio = 8, // Setting should be presented in a radio group
59 61
60 Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage 62 Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
61}; 63};
@@ -67,6 +69,7 @@ public:
67 explicit Linkage(u32 initial_count = 0); 69 explicit Linkage(u32 initial_count = 0);
68 ~Linkage(); 70 ~Linkage();
69 std::map<Category, std::vector<BasicSetting*>> by_category{}; 71 std::map<Category, std::vector<BasicSetting*>> by_category{};
72 std::map<std::string, Settings::BasicSetting*> by_key{};
70 std::vector<std::function<void()>> restore_functions{}; 73 std::vector<std::function<void()>> restore_functions{};
71 u32 count; 74 u32 count;
72}; 75};
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index e7cb59ea5..815cafe15 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -146,6 +146,8 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
146 146
147ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); 147ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
148 148
149ENUM(ConsoleMode, Handheld, Docked);
150
149template <typename Type> 151template <typename Type>
150inline std::string CanonicalizeEnum(Type id) { 152inline std::string CanonicalizeEnum(Type id) {
151 const auto group = EnumMetadata<Type>::Canonicalizations(); 153 const auto group = EnumMetadata<Type>::Canonicalizations();
diff --git a/src/common/swap.h b/src/common/swap.h
index 085baaf9a..fde343e45 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -460,11 +460,6 @@ S operator&(const S& i, const swap_struct_t<T, F> v) {
460 return i & v.swap(); 460 return i & v.swap();
461} 461}
462 462
463template <typename S, typename T, typename F>
464S operator&(const swap_struct_t<T, F> v, const S& i) {
465 return static_cast<S>(v.swap() & i);
466}
467
468// Comparison 463// Comparison
469template <typename S, typename T, typename F> 464template <typename S, typename T, typename F>
470bool operator<(const S& p, const swap_struct_t<T, F> v) { 465bool operator<(const S& p, const swap_struct_t<T, F> v) {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 012648d69..c33910ade 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -584,13 +584,23 @@ add_library(core STATIC
584 hle/service/lm/lm.h 584 hle/service/lm/lm.h
585 hle/service/mig/mig.cpp 585 hle/service/mig/mig.cpp
586 hle/service/mig/mig.h 586 hle/service/mig/mig.h
587 hle/service/mii/types/char_info.cpp
588 hle/service/mii/types/char_info.h
589 hle/service/mii/types/core_data.cpp
590 hle/service/mii/types/core_data.h
591 hle/service/mii/types/raw_data.cpp
592 hle/service/mii/types/raw_data.h
593 hle/service/mii/types/store_data.cpp
594 hle/service/mii/types/store_data.h
595 hle/service/mii/types/ver3_store_data.cpp
596 hle/service/mii/types/ver3_store_data.h
587 hle/service/mii/mii.cpp 597 hle/service/mii/mii.cpp
588 hle/service/mii/mii.h 598 hle/service/mii/mii.h
589 hle/service/mii/mii_manager.cpp 599 hle/service/mii/mii_manager.cpp
590 hle/service/mii/mii_manager.h 600 hle/service/mii/mii_manager.h
591 hle/service/mii/raw_data.cpp 601 hle/service/mii/mii_result.h
592 hle/service/mii/raw_data.h 602 hle/service/mii/mii_types.h
593 hle/service/mii/types.h 603 hle/service/mii/mii_util.h
594 hle/service/mm/mm_u.cpp 604 hle/service/mm/mm_u.cpp
595 hle/service/mm/mm_u.h 605 hle/service/mm/mm_u.h
596 hle/service/mnpp/mnpp_app.cpp 606 hle/service/mnpp/mnpp_app.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9c5246a56..2d6e61398 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -273,7 +273,8 @@ struct System::Impl {
273 time_manager.Initialize(); 273 time_manager.Initialize();
274 274
275 is_powered_on = true; 275 is_powered_on = true;
276 exit_lock = false; 276 exit_locked = false;
277 exit_requested = false;
277 278
278 microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); 279 microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0);
279 microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); 280 microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1);
@@ -398,7 +399,8 @@ struct System::Impl {
398 } 399 }
399 400
400 is_powered_on = false; 401 is_powered_on = false;
401 exit_lock = false; 402 exit_locked = false;
403 exit_requested = false;
402 404
403 if (gpu_core != nullptr) { 405 if (gpu_core != nullptr) {
404 gpu_core->NotifyShutdown(); 406 gpu_core->NotifyShutdown();
@@ -509,7 +511,8 @@ struct System::Impl {
509 511
510 CpuManager cpu_manager; 512 CpuManager cpu_manager;
511 std::atomic_bool is_powered_on{}; 513 std::atomic_bool is_powered_on{};
512 bool exit_lock = false; 514 bool exit_locked = false;
515 bool exit_requested = false;
513 516
514 bool nvdec_active{}; 517 bool nvdec_active{};
515 518
@@ -561,6 +564,8 @@ struct System::Impl {
561 564
562 std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> 565 std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
563 gpu_dirty_memory_write_manager{}; 566 gpu_dirty_memory_write_manager{};
567
568 std::deque<std::vector<u8>> user_channel;
564}; 569};
565 570
566System::System() : impl{std::make_unique<Impl>(*this)} {} 571System::System() : impl{std::make_unique<Impl>(*this)} {}
@@ -945,12 +950,20 @@ const Service::Time::TimeManager& System::GetTimeManager() const {
945 return impl->time_manager; 950 return impl->time_manager;
946} 951}
947 952
948void System::SetExitLock(bool locked) { 953void System::SetExitLocked(bool locked) {
949 impl->exit_lock = locked; 954 impl->exit_locked = locked;
955}
956
957bool System::GetExitLocked() const {
958 return impl->exit_locked;
950} 959}
951 960
952bool System::GetExitLock() const { 961void System::SetExitRequested(bool requested) {
953 return impl->exit_lock; 962 impl->exit_requested = requested;
963}
964
965bool System::GetExitRequested() const {
966 return impl->exit_requested;
954} 967}
955 968
956void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { 969void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) {
@@ -1027,6 +1040,10 @@ void System::ExecuteProgram(std::size_t program_index) {
1027 } 1040 }
1028} 1041}
1029 1042
1043std::deque<std::vector<u8>>& System::GetUserChannel() {
1044 return impl->user_channel;
1045}
1046
1030void System::RegisterExitCallback(ExitCallback&& callback) { 1047void System::RegisterExitCallback(ExitCallback&& callback) {
1031 impl->exit_callback = std::move(callback); 1048 impl->exit_callback = std::move(callback);
1032} 1049}
diff --git a/src/core/core.h b/src/core/core.h
index c70ea1965..fba312125 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <cstddef> 6#include <cstddef>
7#include <deque>
7#include <functional> 8#include <functional>
8#include <memory> 9#include <memory>
9#include <mutex> 10#include <mutex>
@@ -412,8 +413,11 @@ public:
412 /// Gets an immutable reference to the Room Network. 413 /// Gets an immutable reference to the Room Network.
413 [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; 414 [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
414 415
415 void SetExitLock(bool locked); 416 void SetExitLocked(bool locked);
416 [[nodiscard]] bool GetExitLock() const; 417 bool GetExitLocked() const;
418
419 void SetExitRequested(bool requested);
420 bool GetExitRequested() const;
417 421
418 void SetApplicationProcessBuildID(const CurrentBuildProcessID& id); 422 void SetApplicationProcessBuildID(const CurrentBuildProcessID& id);
419 [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const; 423 [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const;
@@ -456,6 +460,12 @@ public:
456 */ 460 */
457 void ExecuteProgram(std::size_t program_index); 461 void ExecuteProgram(std::size_t program_index);
458 462
463 /**
464 * Gets a reference to the user channel stack.
465 * It is used to transfer data between programs.
466 */
467 [[nodiscard]] std::deque<std::vector<u8>>& GetUserChannel();
468
459 /// Type used for the frontend to designate a callback for System to exit the application. 469 /// Type used for the frontend to designate a callback for System to exit the application.
460 using ExitCallback = std::function<void()>; 470 using ExitCallback = std::function<void()>;
461 471
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 4ff2c50e5..e13c5cdc7 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -35,7 +35,6 @@ namespace Core::Crypto {
35namespace { 35namespace {
36 36
37constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 37constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
38constexpr u64 FULL_TICKET_SIZE = 0x400;
39 38
40using Common::AsArray; 39using Common::AsArray;
41 40
@@ -156,6 +155,10 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
156 UNREACHABLE(); 155 UNREACHABLE();
157} 156}
158 157
158bool Ticket::IsValid() const {
159 return !std::holds_alternative<std::monostate>(data);
160}
161
159SignatureType Ticket::GetSignatureType() const { 162SignatureType Ticket::GetSignatureType() const {
160 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { 163 if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) {
161 return ticket->sig_type; 164 return ticket->sig_type;
@@ -210,6 +213,54 @@ Ticket Ticket::SynthesizeCommon(Key128 title_key, const std::array<u8, 16>& righ
210 return Ticket{out}; 213 return Ticket{out};
211} 214}
212 215
216Ticket Ticket::Read(const FileSys::VirtualFile& file) {
217 // Attempt to read up to the largest ticket size, and make sure we read at least a signature
218 // type.
219 std::array<u8, sizeof(RSA4096Ticket)> raw_data{};
220 auto read_size = file->Read(raw_data.data(), raw_data.size(), 0);
221 if (read_size < sizeof(SignatureType)) {
222 LOG_WARNING(Crypto, "Attempted to read ticket file with invalid size {}.", read_size);
223 return Ticket{std::monostate()};
224 }
225 return Read(std::span{raw_data});
226}
227
228Ticket Ticket::Read(std::span<const u8> raw_data) {
229 // Some tools read only 0x180 bytes of ticket data instead of 0x2C0, so
230 // just make sure we have at least the bare minimum of data to work with.
231 SignatureType sig_type;
232 if (raw_data.size() < sizeof(SignatureType)) {
233 LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid size {}.",
234 raw_data.size());
235 return Ticket{std::monostate()};
236 }
237 std::memcpy(&sig_type, raw_data.data(), sizeof(sig_type));
238
239 switch (sig_type) {
240 case SignatureType::RSA_4096_SHA1:
241 case SignatureType::RSA_4096_SHA256: {
242 RSA4096Ticket ticket{};
243 std::memcpy(&ticket, raw_data.data(), sizeof(ticket));
244 return Ticket{ticket};
245 }
246 case SignatureType::RSA_2048_SHA1:
247 case SignatureType::RSA_2048_SHA256: {
248 RSA2048Ticket ticket{};
249 std::memcpy(&ticket, raw_data.data(), sizeof(ticket));
250 return Ticket{ticket};
251 }
252 case SignatureType::ECDSA_SHA1:
253 case SignatureType::ECDSA_SHA256: {
254 ECDSATicket ticket{};
255 std::memcpy(&ticket, raw_data.data(), sizeof(ticket));
256 return Ticket{ticket};
257 }
258 default:
259 LOG_WARNING(Crypto, "Attempted to parse ticket buffer with invalid type {}.", sig_type);
260 return Ticket{std::monostate()};
261 }
262}
263
213Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { 264Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
214 Key128 out{}; 265 Key128 out{};
215 266
@@ -290,9 +341,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
290 } 341 }
291} 342}
292 343
293RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { 344void KeyManager::DeriveETicketRSAKey() {
294 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) { 345 if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) {
295 return {}; 346 return;
296 } 347 }
297 348
298 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); 349 const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
@@ -304,12 +355,12 @@ RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
304 rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, 355 rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
305 extended_dec.data(), Op::Decrypt); 356 extended_dec.data(), Op::Decrypt);
306 357
307 RSAKeyPair<2048> rsa_key{}; 358 std::memcpy(eticket_rsa_keypair.decryption_key.data(), extended_dec.data(),
308 std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); 359 eticket_rsa_keypair.decryption_key.size());
309 std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); 360 std::memcpy(eticket_rsa_keypair.modulus.data(), extended_dec.data() + 0x100,
310 std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); 361 eticket_rsa_keypair.modulus.size());
311 362 std::memcpy(eticket_rsa_keypair.exponent.data(), extended_dec.data() + 0x200,
312 return rsa_key; 363 eticket_rsa_keypair.exponent.size());
313} 364}
314 365
315Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { 366Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
@@ -447,10 +498,12 @@ std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) {
447 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { 498 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
448 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && 499 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
449 buffer[offset + 3] == 0x0) { 500 buffer[offset + 3] == 0x0) {
450 out.emplace_back(); 501 // NOTE: Assumes ticket blob will only contain RSA-2048 tickets.
451 auto& next = out.back(); 502 auto ticket = Ticket::Read(std::span{buffer.data() + offset, sizeof(RSA2048Ticket)});
452 std::memcpy(&next, buffer.data() + offset, sizeof(Ticket)); 503 offset += sizeof(RSA2048Ticket);
453 offset += FULL_TICKET_SIZE; 504 if (ticket.IsValid()) {
505 out.push_back(ticket);
506 }
454 } 507 }
455 } 508 }
456 509
@@ -503,25 +556,36 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
503 return offset; 556 return offset;
504} 557}
505 558
506std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 559std::optional<Key128> KeyManager::ParseTicketTitleKey(const Ticket& ticket) {
507 const RSAKeyPair<2048>& key) { 560 if (!ticket.IsValid()) {
561 LOG_WARNING(Crypto, "Attempted to parse title key of invalid ticket.");
562 return std::nullopt;
563 }
564
565 if (ticket.GetData().rights_id == Key128{}) {
566 LOG_WARNING(Crypto, "Attempted to parse title key of ticket with no rights ID.");
567 return std::nullopt;
568 }
569
508 const auto issuer = ticket.GetData().issuer; 570 const auto issuer = ticket.GetData().issuer;
509 if (IsAllZeroArray(issuer)) { 571 if (IsAllZeroArray(issuer)) {
572 LOG_WARNING(Crypto, "Attempted to parse title key of ticket with invalid issuer.");
510 return std::nullopt; 573 return std::nullopt;
511 } 574 }
575
512 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { 576 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
513 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); 577 LOG_WARNING(Crypto, "Parsing ticket with non-standard certificate authority.");
514 } 578 }
515 579
516 Key128 rights_id = ticket.GetData().rights_id; 580 if (ticket.GetData().type == TitleKeyType::Common) {
517 581 return ticket.GetData().title_key_common;
518 if (rights_id == Key128{}) {
519 return std::nullopt;
520 } 582 }
521 583
522 if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), 584 if (eticket_rsa_keypair == RSAKeyPair<2048>{}) {
523 ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { 585 LOG_WARNING(
524 return std::make_pair(rights_id, ticket.GetData().title_key_common); 586 Crypto,
587 "Skipping personalized ticket title key parsing due to missing ETicket RSA key-pair.");
588 return std::nullopt;
525 } 589 }
526 590
527 mbedtls_mpi D; // RSA Private Exponent 591 mbedtls_mpi D; // RSA Private Exponent
@@ -534,9 +598,12 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
534 mbedtls_mpi_init(&S); 598 mbedtls_mpi_init(&S);
535 mbedtls_mpi_init(&M); 599 mbedtls_mpi_init(&M);
536 600
537 mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); 601 const auto& title_key_block = ticket.GetData().title_key_block;
538 mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); 602 mbedtls_mpi_read_binary(&D, eticket_rsa_keypair.decryption_key.data(),
539 mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100); 603 eticket_rsa_keypair.decryption_key.size());
604 mbedtls_mpi_read_binary(&N, eticket_rsa_keypair.modulus.data(),
605 eticket_rsa_keypair.modulus.size());
606 mbedtls_mpi_read_binary(&S, title_key_block.data(), title_key_block.size());
540 607
541 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); 608 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
542 609
@@ -564,8 +631,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
564 631
565 Key128 key_temp{}; 632 Key128 key_temp{};
566 std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); 633 std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
567 634 return key_temp;
568 return std::make_pair(rights_id, key_temp);
569} 635}
570 636
571KeyManager::KeyManager() { 637KeyManager::KeyManager() {
@@ -669,6 +735,14 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
669 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); 735 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
670 } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { 736 } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) {
671 eticket_extended_kek = Common::HexStringToArray<576>(out[1]); 737 eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
738 } else if (out[0].compare(0, 19, "eticket_rsa_keypair") == 0) {
739 const auto key_data = Common::HexStringToArray<528>(out[1]);
740 std::memcpy(eticket_rsa_keypair.decryption_key.data(), key_data.data(),
741 eticket_rsa_keypair.decryption_key.size());
742 std::memcpy(eticket_rsa_keypair.modulus.data(), key_data.data() + 0x100,
743 eticket_rsa_keypair.modulus.size());
744 std::memcpy(eticket_rsa_keypair.exponent.data(), key_data.data() + 0x200,
745 eticket_rsa_keypair.exponent.size());
672 } else { 746 } else {
673 for (const auto& kv : KEYS_VARIABLE_LENGTH) { 747 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
674 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) { 748 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) {
@@ -1110,56 +1184,38 @@ void KeyManager::DeriveETicket(PartitionDataManager& data,
1110 1184
1111 eticket_extended_kek = data.GetETicketExtendedKek(); 1185 eticket_extended_kek = data.GetETicketExtendedKek();
1112 WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); 1186 WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek);
1187 DeriveETicketRSAKey();
1113 PopulateTickets(); 1188 PopulateTickets();
1114} 1189}
1115 1190
1116void KeyManager::PopulateTickets() { 1191void KeyManager::PopulateTickets() {
1117 const auto rsa_key = GetETicketRSAKey(); 1192 if (ticket_databases_loaded) {
1118
1119 if (rsa_key == RSAKeyPair<2048>{}) {
1120 return; 1193 return;
1121 } 1194 }
1195 ticket_databases_loaded = true;
1122 1196
1123 if (!common_tickets.empty() && !personal_tickets.empty()) { 1197 std::vector<Ticket> tickets;
1124 return;
1125 }
1126 1198
1127 const auto system_save_e1_path = 1199 const auto system_save_e1_path =
1128 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1"; 1200 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1";
1129 1201 if (Common::FS::Exists(system_save_e1_path)) {
1130 const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, 1202 const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read,
1131 Common::FS::FileType::BinaryFile}; 1203 Common::FS::FileType::BinaryFile};
1204 const auto blob1 = GetTicketblob(save_e1);
1205 tickets.insert(tickets.end(), blob1.begin(), blob1.end());
1206 }
1132 1207
1133 const auto system_save_e2_path = 1208 const auto system_save_e2_path =
1134 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2"; 1209 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2";
1210 if (Common::FS::Exists(system_save_e2_path)) {
1211 const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read,
1212 Common::FS::FileType::BinaryFile};
1213 const auto blob2 = GetTicketblob(save_e2);
1214 tickets.insert(tickets.end(), blob2.begin(), blob2.end());
1215 }
1135 1216
1136 const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, 1217 for (const auto& ticket : tickets) {
1137 Common::FS::FileType::BinaryFile}; 1218 AddTicket(ticket);
1138
1139 const auto blob2 = GetTicketblob(save_e2);
1140 auto res = GetTicketblob(save_e1);
1141
1142 const auto idx = res.size();
1143 res.insert(res.end(), blob2.begin(), blob2.end());
1144
1145 for (std::size_t i = 0; i < res.size(); ++i) {
1146 const auto common = i < idx;
1147 const auto pair = ParseTicket(res[i], rsa_key);
1148 if (!pair) {
1149 continue;
1150 }
1151
1152 const auto& [rid, key] = *pair;
1153 u128 rights_id;
1154 std::memcpy(rights_id.data(), rid.data(), rid.size());
1155
1156 if (common) {
1157 common_tickets[rights_id] = res[i];
1158 } else {
1159 personal_tickets[rights_id] = res[i];
1160 }
1161
1162 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
1163 } 1219 }
1164} 1220}
1165 1221
@@ -1291,41 +1347,33 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
1291 return personal_tickets; 1347 return personal_tickets;
1292} 1348}
1293 1349
1294bool KeyManager::AddTicketCommon(Ticket raw) { 1350bool KeyManager::AddTicket(const Ticket& ticket) {
1295 const auto rsa_key = GetETicketRSAKey(); 1351 if (!ticket.IsValid()) {
1296 if (rsa_key == RSAKeyPair<2048>{}) { 1352 LOG_WARNING(Crypto, "Attempted to add invalid ticket.");
1297 return false;
1298 }
1299
1300 const auto pair = ParseTicket(raw, rsa_key);
1301 if (!pair) {
1302 return false; 1353 return false;
1303 } 1354 }
1304 1355
1305 const auto& [rid, key] = *pair; 1356 const auto& rid = ticket.GetData().rights_id;
1306 u128 rights_id; 1357 u128 rights_id;
1307 std::memcpy(rights_id.data(), rid.data(), rid.size()); 1358 std::memcpy(rights_id.data(), rid.data(), rid.size());
1308 common_tickets[rights_id] = raw; 1359 if (ticket.GetData().type == Core::Crypto::TitleKeyType::Common) {
1309 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); 1360 common_tickets[rights_id] = ticket;
1310 return true; 1361 } else {
1311} 1362 personal_tickets[rights_id] = ticket;
1363 }
1312 1364
1313bool KeyManager::AddTicketPersonalized(Ticket raw) { 1365 if (HasKey(S128KeyType::Titlekey, rights_id[1], rights_id[0])) {
1314 const auto rsa_key = GetETicketRSAKey(); 1366 LOG_DEBUG(Crypto,
1315 if (rsa_key == RSAKeyPair<2048>{}) { 1367 "Skipping parsing title key from ticket for known rights ID {:016X}{:016X}.",
1316 return false; 1368 rights_id[1], rights_id[0]);
1369 return true;
1317 } 1370 }
1318 1371
1319 const auto pair = ParseTicket(raw, rsa_key); 1372 const auto key = ParseTicketTitleKey(ticket);
1320 if (!pair) { 1373 if (!key) {
1321 return false; 1374 return false;
1322 } 1375 }
1323 1376 SetKey(S128KeyType::Titlekey, key.value(), rights_id[1], rights_id[0]);
1324 const auto& [rid, key] = *pair;
1325 u128 rights_id;
1326 std::memcpy(rights_id.data(), rid.data(), rid.size());
1327 common_tickets[rights_id] = raw;
1328 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
1329 return true; 1377 return true;
1330} 1378}
1331} // namespace Core::Crypto 1379} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 8c864503b..2250eccec 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -7,6 +7,7 @@
7#include <filesystem> 7#include <filesystem>
8#include <map> 8#include <map>
9#include <optional> 9#include <optional>
10#include <span>
10#include <string> 11#include <string>
11 12
12#include <variant> 13#include <variant>
@@ -29,8 +30,6 @@ enum class ResultStatus : u16;
29 30
30namespace Core::Crypto { 31namespace Core::Crypto {
31 32
32constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
33
34using Key128 = std::array<u8, 0x10>; 33using Key128 = std::array<u8, 0x10>;
35using Key256 = std::array<u8, 0x20>; 34using Key256 = std::array<u8, 0x20>;
36using SHA256Hash = std::array<u8, 0x20>; 35using SHA256Hash = std::array<u8, 0x20>;
@@ -82,6 +81,7 @@ struct RSA4096Ticket {
82 INSERT_PADDING_BYTES(0x3C); 81 INSERT_PADDING_BYTES(0x3C);
83 TicketData data; 82 TicketData data;
84}; 83};
84static_assert(sizeof(RSA4096Ticket) == 0x500, "RSA4096Ticket has incorrect size.");
85 85
86struct RSA2048Ticket { 86struct RSA2048Ticket {
87 SignatureType sig_type; 87 SignatureType sig_type;
@@ -89,6 +89,7 @@ struct RSA2048Ticket {
89 INSERT_PADDING_BYTES(0x3C); 89 INSERT_PADDING_BYTES(0x3C);
90 TicketData data; 90 TicketData data;
91}; 91};
92static_assert(sizeof(RSA2048Ticket) == 0x400, "RSA2048Ticket has incorrect size.");
92 93
93struct ECDSATicket { 94struct ECDSATicket {
94 SignatureType sig_type; 95 SignatureType sig_type;
@@ -96,16 +97,41 @@ struct ECDSATicket {
96 INSERT_PADDING_BYTES(0x40); 97 INSERT_PADDING_BYTES(0x40);
97 TicketData data; 98 TicketData data;
98}; 99};
100static_assert(sizeof(ECDSATicket) == 0x340, "ECDSATicket has incorrect size.");
99 101
100struct Ticket { 102struct Ticket {
101 std::variant<RSA4096Ticket, RSA2048Ticket, ECDSATicket> data; 103 std::variant<std::monostate, RSA4096Ticket, RSA2048Ticket, ECDSATicket> data;
102 104
103 SignatureType GetSignatureType() const; 105 [[nodiscard]] bool IsValid() const;
104 TicketData& GetData(); 106 [[nodiscard]] SignatureType GetSignatureType() const;
105 const TicketData& GetData() const; 107 [[nodiscard]] TicketData& GetData();
106 u64 GetSize() const; 108 [[nodiscard]] const TicketData& GetData() const;
107 109 [[nodiscard]] u64 GetSize() const;
110
111 /**
112 * Synthesizes a common ticket given a title key and rights ID.
113 *
114 * @param title_key Title key to store in the ticket.
115 * @param rights_id Rights ID the ticket is for.
116 * @return The synthesized common ticket.
117 */
108 static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id); 118 static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id);
119
120 /**
121 * Reads a ticket from a file.
122 *
123 * @param file File to read the ticket from.
124 * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false.
125 */
126 static Ticket Read(const FileSys::VirtualFile& file);
127
128 /**
129 * Reads a ticket from a memory buffer.
130 *
131 * @param raw_data Buffer to read the ticket from.
132 * @return The read ticket. If the ticket data is invalid, Ticket::IsValid() will be false.
133 */
134 static Ticket Read(std::span<const u8> raw_data);
109}; 135};
110 136
111static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); 137static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
@@ -264,8 +290,7 @@ public:
264 const std::map<u128, Ticket>& GetCommonTickets() const; 290 const std::map<u128, Ticket>& GetCommonTickets() const;
265 const std::map<u128, Ticket>& GetPersonalizedTickets() const; 291 const std::map<u128, Ticket>& GetPersonalizedTickets() const;
266 292
267 bool AddTicketCommon(Ticket raw); 293 bool AddTicket(const Ticket& ticket);
268 bool AddTicketPersonalized(Ticket raw);
269 294
270 void ReloadKeys(); 295 void ReloadKeys();
271 bool AreKeysLoaded() const; 296 bool AreKeysLoaded() const;
@@ -279,10 +304,12 @@ private:
279 // Map from rights ID to ticket 304 // Map from rights ID to ticket
280 std::map<u128, Ticket> common_tickets; 305 std::map<u128, Ticket> common_tickets;
281 std::map<u128, Ticket> personal_tickets; 306 std::map<u128, Ticket> personal_tickets;
307 bool ticket_databases_loaded = false;
282 308
283 std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; 309 std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
284 std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; 310 std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
285 std::array<u8, 576> eticket_extended_kek{}; 311 std::array<u8, 576> eticket_extended_kek{};
312 RSAKeyPair<2048> eticket_rsa_keypair{};
286 313
287 bool dev_mode; 314 bool dev_mode;
288 void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); 315 void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys);
@@ -293,10 +320,13 @@ private:
293 320
294 void DeriveGeneralPurposeKeys(std::size_t crypto_revision); 321 void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
295 322
296 RSAKeyPair<2048> GetETicketRSAKey() const; 323 void DeriveETicketRSAKey();
297 324
298 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 325 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
299 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 326 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
327
328 /// Parses the title key section of a ticket.
329 std::optional<Key128> ParseTicketTitleKey(const Ticket& ticket);
300}; 330};
301 331
302Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 332Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
@@ -311,9 +341,4 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
311 341
312std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save); 342std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save);
313 343
314// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
315// (offset 0x140-0x144 is zero)
316std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
317 const RSAKeyPair<2048>& eticket_extended_key);
318
319} // namespace Core::Crypto 344} // namespace Core::Crypto
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 44e6852fe..7d2f0abb8 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -22,6 +22,10 @@
22 22
23namespace FileSys { 23namespace FileSys {
24 24
25static u8 MasterKeyIdForKeyGeneration(u8 key_generation) {
26 return std::max<u8>(key_generation, 1) - 1;
27}
28
25NCA::NCA(VirtualFile file_, const NCA* base_nca) 29NCA::NCA(VirtualFile file_, const NCA* base_nca)
26 : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { 30 : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
27 if (file == nullptr) { 31 if (file == nullptr) {
@@ -41,12 +45,17 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
41 return; 45 return;
42 } 46 }
43 47
48 // Ensure we have the proper key area keys to continue.
49 const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration());
50 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) {
51 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
52 return;
53 }
54
44 RightsId rights_id{}; 55 RightsId rights_id{};
45 reader->GetRightsId(rights_id.data(), rights_id.size()); 56 reader->GetRightsId(rights_id.data(), rights_id.size());
46 if (rights_id != RightsId{}) { 57 if (rights_id != RightsId{}) {
47 // External decryption key required; provide it here. 58 // External decryption key required; provide it here.
48 const auto key_generation = std::max<s32>(reader->GetKeyGeneration(), 1) - 1;
49
50 u128 rights_id_u128; 59 u128 rights_id_u128;
51 std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id)); 60 std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id));
52 61
@@ -57,12 +66,12 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
57 return; 66 return;
58 } 67 }
59 68
60 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, key_generation)) { 69 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
61 status = Loader::ResultStatus::ErrorMissingTitlekek; 70 status = Loader::ResultStatus::ErrorMissingTitlekek;
62 return; 71 return;
63 } 72 }
64 73
65 auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, key_generation); 74 auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id);
66 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB); 75 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB);
67 cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), 76 cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(),
68 Core::Crypto::Op::Decrypt); 77 Core::Crypto::Op::Decrypt);
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 52c78020c..f4a774675 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -45,6 +45,10 @@ CNMT::CNMT(CNMTHeader header_, OptionalHeader opt_header_,
45 45
46CNMT::~CNMT() = default; 46CNMT::~CNMT() = default;
47 47
48const CNMTHeader& CNMT::GetHeader() const {
49 return header;
50}
51
48u64 CNMT::GetTitleID() const { 52u64 CNMT::GetTitleID() const {
49 return header.title_id; 53 return header.title_id;
50} 54}
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index c59ece010..68e463b5f 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -89,6 +89,7 @@ public:
89 std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_); 89 std::vector<ContentRecord> content_records_, std::vector<MetaRecord> meta_records_);
90 ~CNMT(); 90 ~CNMT();
91 91
92 const CNMTHeader& GetHeader() const;
92 u64 GetTitleID() const; 93 u64 GetTitleID() const;
93 u32 GetTitleVersion() const; 94 u32 GetTitleVersion() const;
94 TitleType GetType() const; 95 TitleType GetType() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index f70adab82..e33b00d89 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -9,6 +9,7 @@
9#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
10#include "common/hex_util.h" 10#include "common/hex_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/scope_exit.h"
12#include "core/crypto/key_manager.h" 13#include "core/crypto/key_manager.h"
13#include "core/file_sys/card_image.h" 14#include "core/file_sys/card_image.h"
14#include "core/file_sys/common_funcs.h" 15#include "core/file_sys/common_funcs.h"
@@ -625,7 +626,7 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
625 nca->GetTitleId() != title_id) { 626 nca->GetTitleId() != title_id) {
626 // Create fake cnmt for patch to multiprogram application 627 // Create fake cnmt for patch to multiprogram application
627 const auto sub_nca_result = 628 const auto sub_nca_result =
628 InstallEntry(*nca, TitleType::Update, overwrite_if_exists, copy); 629 InstallEntry(*nca, cnmt.GetHeader(), record, overwrite_if_exists, copy);
629 if (sub_nca_result != InstallResult::Success) { 630 if (sub_nca_result != InstallResult::Success) {
630 return sub_nca_result; 631 return sub_nca_result;
631 } 632 }
@@ -672,6 +673,31 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
672 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); 673 return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id);
673} 674}
674 675
676InstallResult RegisteredCache::InstallEntry(const NCA& nca, const CNMTHeader& base_header,
677 const ContentRecord& base_record,
678 bool overwrite_if_exists, const VfsCopyFunction& copy) {
679 const CNMTHeader header{
680 .title_id = nca.GetTitleId(),
681 .title_version = base_header.title_version,
682 .type = base_header.type,
683 .reserved = {},
684 .table_offset = 0x10,
685 .number_content_entries = 1,
686 .number_meta_entries = 0,
687 .attributes = 0,
688 .reserved2 = {},
689 .is_committed = 0,
690 .required_download_system_version = 0,
691 .reserved3 = {},
692 };
693 const OptionalHeader opt_header{0, 0};
694 const CNMT new_cnmt(header, opt_header, {base_record}, {});
695 if (!RawInstallYuzuMeta(new_cnmt)) {
696 return InstallResult::ErrorMetaFailed;
697 }
698 return RawInstallNCA(nca, copy, overwrite_if_exists, base_record.nca_id);
699}
700
675bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { 701bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
676 bool removed_data = false; 702 bool removed_data = false;
677 703
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index bd7f53eaf..64815a845 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -24,6 +24,7 @@ enum class NCAContentType : u8;
24enum class TitleType : u8; 24enum class TitleType : u8;
25 25
26struct ContentRecord; 26struct ContentRecord;
27struct CNMTHeader;
27struct MetaRecord; 28struct MetaRecord;
28class RegisteredCache; 29class RegisteredCache;
29 30
@@ -169,6 +170,10 @@ public:
169 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, 170 InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
170 const VfsCopyFunction& copy = &VfsRawCopy); 171 const VfsCopyFunction& copy = &VfsRawCopy);
171 172
173 InstallResult InstallEntry(const NCA& nca, const CNMTHeader& base_header,
174 const ContentRecord& base_record, bool overwrite_if_exists = false,
175 const VfsCopyFunction& copy = &VfsRawCopy);
176
172 // Removes an existing entry based on title id 177 // Removes an existing entry based on title id
173 bool RemoveExistingEntry(u64 title_id) const; 178 bool RemoveExistingEntry(u64 title_id) const;
174 179
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index e1e89ce2d..68e8ec22f 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -164,24 +164,6 @@ VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType titl
164 return nullptr; 164 return nullptr;
165} 165}
166 166
167std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const {
168 if (extracted)
169 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
170 std::vector<Core::Crypto::Key128> out;
171 for (const auto& ticket_file : ticket_files) {
172 if (ticket_file == nullptr ||
173 ticket_file->GetSize() <
174 Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
175 continue;
176 }
177
178 out.emplace_back();
179 ticket_file->Read(out.back().data(), out.back().size(),
180 Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
181 }
182 return out;
183}
184
185std::vector<VirtualFile> NSP::GetFiles() const { 167std::vector<VirtualFile> NSP::GetFiles() const {
186 return pfs->GetFiles(); 168 return pfs->GetFiles();
187} 169}
@@ -208,22 +190,11 @@ void NSP::SetTicketKeys(const std::vector<VirtualFile>& files) {
208 continue; 190 continue;
209 } 191 }
210 192
211 if (ticket_file->GetSize() < 193 auto ticket = Core::Crypto::Ticket::Read(ticket_file);
212 Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { 194 if (!keys.AddTicket(ticket)) {
195 LOG_WARNING(Common_Filesystem, "Could not load NSP ticket {}", ticket_file->GetName());
213 continue; 196 continue;
214 } 197 }
215
216 Core::Crypto::Key128 key{};
217 ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
218
219 // We get the name without the extension in order to create the rights ID.
220 std::string name_only(ticket_file->GetName());
221 name_only.erase(name_only.size() - 4);
222
223 const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
224 u128 rights_id;
225 std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
226 keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
227 } 198 }
228} 199}
229 200
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 27f97c725..915bffca9 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -53,7 +53,6 @@ public:
53 TitleType title_type = TitleType::Application) const; 53 TitleType title_type = TitleType::Application) const;
54 VirtualFile GetNCAFile(u64 title_id, ContentRecordType type, 54 VirtualFile GetNCAFile(u64 title_id, ContentRecordType type,
55 TitleType title_type = TitleType::Application) const; 55 TitleType title_type = TitleType::Application) const;
56 std::vector<Core::Crypto::Key128> GetTitlekey() const;
57 56
58 std::vector<VirtualFile> GetFiles() const override; 57 std::vector<VirtualFile> GetFiles() const override;
59 58
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 3300d4f79..27755cb58 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -3,6 +3,8 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "common/settings_enums.h"
6#include "core/frontend/applets/controller.h" 8#include "core/frontend/applets/controller.h"
7#include "core/hid/emulated_controller.h" 9#include "core/hid/emulated_controller.h"
8#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
@@ -62,7 +64,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
62 controller->Connect(true); 64 controller->Connect(true);
63 } 65 }
64 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && 66 } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
65 !Settings::values.use_docked_mode.GetValue()) { 67 !Settings::IsDockedMode()) {
66 // We should *never* reach here under any normal circumstances. 68 // We should *never* reach here under any normal circumstances.
67 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); 69 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
68 controller->Connect(true); 70 controller->Connect(true);
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index b4081fc39..2590b20da 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/settings.h" 7#include "common/settings.h"
8#include "common/settings_enums.h"
8#include "core/frontend/framebuffer_layout.h" 9#include "core/frontend/framebuffer_layout.h"
9 10
10namespace Layout { 11namespace Layout {
@@ -49,7 +50,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
49} 50}
50 51
51FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) { 52FramebufferLayout FrameLayoutFromResolutionScale(f32 res_scale) {
52 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 53 const bool is_docked = Settings::IsDockedMode();
53 const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width; 54 const u32 screen_width = is_docked ? ScreenDocked::Width : ScreenUndocked::Width;
54 const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height; 55 const u32 screen_height = is_docked ? ScreenDocked::Height : ScreenUndocked::Height;
55 56
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
index 90e4e8fb0..e7da7a21d 100644
--- a/src/core/hle/kernel/k_capabilities.cpp
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -156,7 +156,6 @@ Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize; 156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
157 const size_t num_pages = 1; 157 const size_t num_pages = 1;
158 const size_t size = num_pages * PageSize; 158 const size_t size = num_pages * PageSize;
159 R_UNLESS(num_pages != 0, ResultInvalidSize);
160 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress); 159 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
161 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress); 160 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
162 161
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 8d057b3a8..f9c4f9678 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -6,6 +6,7 @@
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring> 7#include <cstring>
8#include "common/settings.h" 8#include "common/settings.h"
9#include "common/settings_enums.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
@@ -45,7 +46,7 @@ constexpr Result ResultNoMessages{ErrorModule::AM, 3};
45constexpr Result ResultInvalidOffset{ErrorModule::AM, 503}; 46constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
46 47
47enum class LaunchParameterKind : u32 { 48enum class LaunchParameterKind : u32 {
48 ApplicationSpecific = 1, 49 UserChannel = 1,
49 AccountPreselectedUser = 2, 50 AccountPreselectedUser = 2,
50}; 51};
51 52
@@ -340,7 +341,7 @@ void ISelfController::Exit(HLERequestContext& ctx) {
340void ISelfController::LockExit(HLERequestContext& ctx) { 341void ISelfController::LockExit(HLERequestContext& ctx) {
341 LOG_DEBUG(Service_AM, "called"); 342 LOG_DEBUG(Service_AM, "called");
342 343
343 system.SetExitLock(true); 344 system.SetExitLocked(true);
344 345
345 IPC::ResponseBuilder rb{ctx, 2}; 346 IPC::ResponseBuilder rb{ctx, 2};
346 rb.Push(ResultSuccess); 347 rb.Push(ResultSuccess);
@@ -349,10 +350,14 @@ void ISelfController::LockExit(HLERequestContext& ctx) {
349void ISelfController::UnlockExit(HLERequestContext& ctx) { 350void ISelfController::UnlockExit(HLERequestContext& ctx) {
350 LOG_DEBUG(Service_AM, "called"); 351 LOG_DEBUG(Service_AM, "called");
351 352
352 system.SetExitLock(false); 353 system.SetExitLocked(false);
353 354
354 IPC::ResponseBuilder rb{ctx, 2}; 355 IPC::ResponseBuilder rb{ctx, 2};
355 rb.Push(ResultSuccess); 356 rb.Push(ResultSuccess);
357
358 if (system.GetExitRequested()) {
359 system.Exit();
360 }
356} 361}
357 362
358void ISelfController::EnterFatalSection(HLERequestContext& ctx) { 363void ISelfController::EnterFatalSection(HLERequestContext& ctx) {
@@ -833,7 +838,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
833 IPC::ResponseBuilder rb{ctx, 4}; 838 IPC::ResponseBuilder rb{ctx, 4};
834 rb.Push(ResultSuccess); 839 rb.Push(ResultSuccess);
835 840
836 if (Settings::values.use_docked_mode.GetValue()) { 841 if (Settings::IsDockedMode()) {
837 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 842 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
838 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 843 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
839 } else { 844 } else {
@@ -921,7 +926,7 @@ void IStorage::Open(HLERequestContext& ctx) {
921} 926}
922 927
923void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) { 928void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
924 const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()}; 929 const bool use_docked_mode{Settings::IsDockedMode()};
925 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); 930 LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
926 931
927 IPC::ResponseBuilder rb{ctx, 3}; 932 IPC::ResponseBuilder rb{ctx, 3};
@@ -1513,27 +1518,26 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
1513 IPC::RequestParser rp{ctx}; 1518 IPC::RequestParser rp{ctx};
1514 const auto kind = rp.PopEnum<LaunchParameterKind>(); 1519 const auto kind = rp.PopEnum<LaunchParameterKind>();
1515 1520
1516 LOG_DEBUG(Service_AM, "called, kind={:08X}", kind); 1521 LOG_INFO(Service_AM, "called, kind={:08X}", kind);
1517 1522
1518 if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) { 1523 if (kind == LaunchParameterKind::UserChannel) {
1519 const auto backend = BCAT::CreateBackendFromSettings(system, [this](u64 tid) { 1524 auto channel = system.GetUserChannel();
1520 return system.GetFileSystemController().GetBCATDirectory(tid); 1525 if (channel.empty()) {
1521 }); 1526 LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
1522 const auto build_id_full = system.GetApplicationProcessBuildID(); 1527 IPC::ResponseBuilder rb{ctx, 2};
1523 u64 build_id{}; 1528 rb.Push(AM::ResultNoDataInChannel);
1524 std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
1525
1526 auto data =
1527 backend->GetLaunchParameter({system.GetApplicationProcessProgramID(), build_id});
1528 if (data.has_value()) {
1529 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1530 rb.Push(ResultSuccess);
1531 rb.PushIpcInterface<IStorage>(system, std::move(*data));
1532 launch_popped_application_specific = true;
1533 return; 1529 return;
1534 } 1530 }
1531
1532 auto data = channel.back();
1533 channel.pop_back();
1534
1535 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1536 rb.Push(ResultSuccess);
1537 rb.PushIpcInterface<IStorage>(system, std::move(data));
1535 } else if (kind == LaunchParameterKind::AccountPreselectedUser && 1538 } else if (kind == LaunchParameterKind::AccountPreselectedUser &&
1536 !launch_popped_account_preselect) { 1539 !launch_popped_account_preselect) {
1540 // TODO: Verify this is hw-accurate
1537 LaunchParameterAccountPreselectedUser params{}; 1541 LaunchParameterAccountPreselectedUser params{};
1538 1542
1539 params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC; 1543 params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
@@ -1545,7 +1549,6 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
1545 params.current_user = *uuid; 1549 params.current_user = *uuid;
1546 1550
1547 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1551 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1548
1549 rb.Push(ResultSuccess); 1552 rb.Push(ResultSuccess);
1550 1553
1551 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); 1554 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
@@ -1553,12 +1556,11 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
1553 1556
1554 rb.PushIpcInterface<IStorage>(system, std::move(buffer)); 1557 rb.PushIpcInterface<IStorage>(system, std::move(buffer));
1555 launch_popped_account_preselect = true; 1558 launch_popped_account_preselect = true;
1556 return; 1559 } else {
1560 LOG_ERROR(Service_AM, "Unknown launch parameter kind.");
1561 IPC::ResponseBuilder rb{ctx, 2};
1562 rb.Push(AM::ResultNoDataInChannel);
1557 } 1563 }
1558
1559 LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
1560 IPC::ResponseBuilder rb{ctx, 2};
1561 rb.Push(AM::ResultNoDataInChannel);
1562} 1564}
1563 1565
1564void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) { 1566void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
@@ -1850,14 +1852,22 @@ void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
1850} 1852}
1851 1853
1852void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) { 1854void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
1853 LOG_WARNING(Service_AM, "(STUBBED) called"); 1855 LOG_DEBUG(Service_AM, "called");
1856
1857 system.GetUserChannel().clear();
1854 1858
1855 IPC::ResponseBuilder rb{ctx, 2}; 1859 IPC::ResponseBuilder rb{ctx, 2};
1856 rb.Push(ResultSuccess); 1860 rb.Push(ResultSuccess);
1857} 1861}
1858 1862
1859void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) { 1863void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
1860 LOG_WARNING(Service_AM, "(STUBBED) called"); 1864 LOG_DEBUG(Service_AM, "called");
1865
1866 IPC::RequestParser rp{ctx};
1867 const auto storage = rp.PopIpcInterface<IStorage>().lock();
1868 if (storage) {
1869 system.GetUserChannel().push_back(storage->GetData());
1870 }
1861 1871
1862 IPC::ResponseBuilder rb{ctx, 2}; 1872 IPC::ResponseBuilder rb{ctx, 2};
1863 rb.Push(ResultSuccess); 1873 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index d68998f04..f75a665b2 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -339,7 +339,6 @@ private:
339 339
340 KernelHelpers::ServiceContext service_context; 340 KernelHelpers::ServiceContext service_context;
341 341
342 bool launch_popped_application_specific = false;
343 bool launch_popped_account_preselect = false; 342 bool launch_popped_account_preselect = false;
344 s32 previous_program_index{-1}; 343 s32 previous_program_index{-1};
345 Kernel::KEvent* gpu_error_detected_event; 344 Kernel::KEvent* gpu_error_detected_event;
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
index d1f652c09..350a90818 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.cpp
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -85,15 +85,18 @@ void MiiEdit::Execute() {
85 break; 85 break;
86 case MiiEditAppletMode::CreateMii: 86 case MiiEditAppletMode::CreateMii:
87 case MiiEditAppletMode::EditMii: { 87 case MiiEditAppletMode::EditMii: {
88 Service::Mii::MiiManager mii_manager; 88 Mii::CharInfo char_info{};
89 Mii::StoreData store_data{};
90 store_data.BuildBase(Mii::Gender::Male);
91 char_info.SetFromStoreData(store_data);
89 92
90 const MiiEditCharInfo char_info{ 93 const MiiEditCharInfo edit_char_info{
91 .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii 94 .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
92 ? applet_input_v4.char_info.mii_info 95 ? applet_input_v4.char_info.mii_info
93 : mii_manager.BuildDefault(0)}, 96 : char_info},
94 }; 97 };
95 98
96 MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info); 99 MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
97 break; 100 break;
98 } 101 }
99 default: 102 default:
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h
index 4705d019f..f3d764073 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit_types.h
+++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h
@@ -7,7 +7,8 @@
7 7
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/service/mii/types.h" 10#include "common/uuid.h"
11#include "core/hle/service/mii/types/char_info.h"
11 12
12namespace Service::AM::Applets { 13namespace Service::AM::Applets {
13 14
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp
index 227fdd0cf..4f1aa5cc2 100644
--- a/src/core/hle/service/apm/apm_controller.cpp
+++ b/src/core/hle/service/apm/apm_controller.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h" 9#include "common/settings.h"
10#include "common/settings_enums.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/hle/service/apm/apm_controller.h" 12#include "core/hle/service/apm/apm_controller.h"
12 13
@@ -67,8 +68,7 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
67} 68}
68 69
69PerformanceMode Controller::GetCurrentPerformanceMode() const { 70PerformanceMode Controller::GetCurrentPerformanceMode() const {
70 return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost 71 return Settings::IsDockedMode() ? PerformanceMode::Boost : PerformanceMode::Normal;
71 : PerformanceMode::Normal;
72} 72}
73 73
74PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { 74PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 526a39130..56fee4591 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -220,7 +220,7 @@ AudInU::AudInU(Core::System& system_)
220AudInU::~AudInU() = default; 220AudInU::~AudInU() = default;
221 221
222void AudInU::ListAudioIns(HLERequestContext& ctx) { 222void AudInU::ListAudioIns(HLERequestContext& ctx) {
223 using namespace AudioCore::AudioRenderer; 223 using namespace AudioCore::Renderer;
224 224
225 LOG_DEBUG(Service_Audio, "called"); 225 LOG_DEBUG(Service_Audio, "called");
226 226
@@ -240,7 +240,7 @@ void AudInU::ListAudioIns(HLERequestContext& ctx) {
240} 240}
241 241
242void AudInU::ListAudioInsAutoFiltered(HLERequestContext& ctx) { 242void AudInU::ListAudioInsAutoFiltered(HLERequestContext& ctx) {
243 using namespace AudioCore::AudioRenderer; 243 using namespace AudioCore::Renderer;
244 244
245 LOG_DEBUG(Service_Audio, "called"); 245 LOG_DEBUG(Service_Audio, "called");
246 246
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 23f84a29f..ca683d72c 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -228,7 +228,7 @@ AudOutU::AudOutU(Core::System& system_)
228AudOutU::~AudOutU() = default; 228AudOutU::~AudOutU() = default;
229 229
230void AudOutU::ListAudioOuts(HLERequestContext& ctx) { 230void AudOutU::ListAudioOuts(HLERequestContext& ctx) {
231 using namespace AudioCore::AudioRenderer; 231 using namespace AudioCore::Renderer;
232 232
233 std::scoped_lock l{impl->mutex}; 233 std::scoped_lock l{impl->mutex};
234 234
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index b723b65c8..2f09cade5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -26,7 +26,7 @@
26#include "core/hle/service/ipc_helpers.h" 26#include "core/hle/service/ipc_helpers.h"
27#include "core/memory.h" 27#include "core/memory.h"
28 28
29using namespace AudioCore::AudioRenderer; 29using namespace AudioCore::Renderer;
30 30
31namespace Service::Audio { 31namespace Service::Audio {
32 32
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index d8e9c8719..3d7993a16 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -28,7 +28,7 @@ private:
28 void GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx); 28 void GetAudioDeviceServiceWithRevisionInfo(HLERequestContext& ctx);
29 29
30 KernelHelpers::ServiceContext service_context; 30 KernelHelpers::ServiceContext service_context;
31 std::unique_ptr<AudioCore::AudioRenderer::Manager> impl; 31 std::unique_ptr<AudioCore::Renderer::Manager> impl;
32 u32 num_audio_devices{0}; 32 u32 num_audio_devices{0};
33}; 33};
34 34
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index fa77007f3..1557e6088 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -174,7 +174,7 @@ public:
174 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, 174 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
175 {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, 175 {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
176 {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, 176 {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
177 {9, nullptr, "DecodeInterleavedForMultiStream"}, 177 {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"},
178 }; 178 };
179 // clang-format on 179 // clang-format on
180 180
@@ -206,6 +206,16 @@ private:
206 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); 206 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
207 } 207 }
208 208
209 void DecodeInterleavedForMultiStream(HLERequestContext& ctx) {
210 LOG_DEBUG(Audio, "called");
211
212 IPC::RequestParser rp{ctx};
213 const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
214 : OpusDecoderState::ExtraBehavior::None;
215
216 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
217 }
218
209 OpusDecoderState decoder_state; 219 OpusDecoderState decoder_state;
210}; 220};
211 221
@@ -257,6 +267,10 @@ void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) {
257 GetWorkBufferSize(ctx); 267 GetWorkBufferSize(ctx);
258} 268}
259 269
270void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) {
271 GetWorkBufferSizeEx(ctx);
272}
273
260void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { 274void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) {
261 OpusMultiStreamParametersEx param; 275 OpusMultiStreamParametersEx param;
262 std::memcpy(&param, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); 276 std::memcpy(&param, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
@@ -354,6 +368,40 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
354 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); 368 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
355} 369}
356 370
371void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
372 OpusMultiStreamParametersEx params;
373 std::memcpy(&params, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
374
375 const auto& sample_rate = params.sample_rate;
376 const auto& channel_count = params.channel_count;
377
378 LOG_INFO(
379 Audio,
380 "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
381 sample_rate, channel_count, params.number_streams, params.number_stereo_streams);
382
383 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
384 sample_rate == 12000 || sample_rate == 8000,
385 "Invalid sample rate");
386
387 int error = 0;
388 OpusDecoderPtr decoder{opus_multistream_decoder_create(
389 sample_rate, static_cast<int>(channel_count), params.number_streams,
390 params.number_stereo_streams, params.channel_mappings.data(), &error)};
391 if (error != OPUS_OK || decoder == nullptr) {
392 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
393 IPC::ResponseBuilder rb{ctx, 2};
394 // TODO(ogniK): Use correct error code
395 rb.Push(ResultUnknown);
396 return;
397 }
398
399 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
400 rb.Push(ResultSuccess);
401 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
402 system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
403}
404
357HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { 405HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
358 static const FunctionInfo functions[] = { 406 static const FunctionInfo functions[] = {
359 {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, 407 {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
@@ -362,9 +410,10 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
362 {3, nullptr, "GetWorkBufferSizeForMultiStream"}, 410 {3, nullptr, "GetWorkBufferSizeForMultiStream"},
363 {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, 411 {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
364 {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, 412 {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
365 {6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"}, 413 {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx,
414 "OpenHardwareOpusDecoderForMultiStreamEx"},
366 {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, 415 {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
367 {8, nullptr, "GetWorkBufferSizeExEx"}, 416 {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"},
368 {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, 417 {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"},
369 }; 418 };
370 RegisterHandlers(functions); 419 RegisterHandlers(functions);
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index ece65c02c..90867bf74 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -18,8 +18,10 @@ struct OpusMultiStreamParametersEx {
18 u32 number_stereo_streams; 18 u32 number_stereo_streams;
19 u32 use_large_frame_size; 19 u32 use_large_frame_size;
20 u32 padding; 20 u32 padding;
21 std::array<u32, 64> channel_mappings; 21 std::array<u8, 0x100> channel_mappings;
22}; 22};
23static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
24 "OpusMultiStreamParametersEx has incorrect size");
23 25
24class HwOpus final : public ServiceFramework<HwOpus> { 26class HwOpus final : public ServiceFramework<HwOpus> {
25public: 27public:
@@ -29,8 +31,10 @@ public:
29private: 31private:
30 void OpenHardwareOpusDecoder(HLERequestContext& ctx); 32 void OpenHardwareOpusDecoder(HLERequestContext& ctx);
31 void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); 33 void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
34 void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);
32 void GetWorkBufferSize(HLERequestContext& ctx); 35 void GetWorkBufferSize(HLERequestContext& ctx);
33 void GetWorkBufferSizeEx(HLERequestContext& ctx); 36 void GetWorkBufferSizeEx(HLERequestContext& ctx);
37 void GetWorkBufferSizeExEx(HLERequestContext& ctx);
34 void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); 38 void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx);
35}; 39};
36 40
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index 446f46b3c..9eaae4c4b 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -122,20 +122,18 @@ private:
122 } 122 }
123 123
124 void ImportTicket(HLERequestContext& ctx) { 124 void ImportTicket(HLERequestContext& ctx) {
125 const auto ticket = ctx.ReadBuffer(); 125 const auto raw_ticket = ctx.ReadBuffer();
126 [[maybe_unused]] const auto cert = ctx.ReadBuffer(1); 126 [[maybe_unused]] const auto cert = ctx.ReadBuffer(1);
127 127
128 if (ticket.size() < sizeof(Core::Crypto::Ticket)) { 128 if (raw_ticket.size() < sizeof(Core::Crypto::Ticket)) {
129 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); 129 LOG_ERROR(Service_ETicket, "The input buffer is not large enough!");
130 IPC::ResponseBuilder rb{ctx, 2}; 130 IPC::ResponseBuilder rb{ctx, 2};
131 rb.Push(ERROR_INVALID_ARGUMENT); 131 rb.Push(ERROR_INVALID_ARGUMENT);
132 return; 132 return;
133 } 133 }
134 134
135 Core::Crypto::Ticket raw{}; 135 Core::Crypto::Ticket ticket = Core::Crypto::Ticket::Read(raw_ticket);
136 std::memcpy(&raw, ticket.data(), sizeof(Core::Crypto::Ticket)); 136 if (!keys.AddTicket(ticket)) {
137
138 if (!keys.AddTicketPersonalized(raw)) {
139 LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); 137 LOG_ERROR(Service_ETicket, "The ticket could not be imported!");
140 IPC::ResponseBuilder rb{ctx, 2}; 138 IPC::ResponseBuilder rb{ctx, 2};
141 rb.Push(ERROR_INVALID_ARGUMENT); 139 rb.Push(ERROR_INVALID_ARGUMENT);
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 03432f7cb..63eecd42b 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -331,7 +331,7 @@ Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties()
331 }; 331 };
332 332
333 // Hack: There is no touch in docked but games still allow it 333 // Hack: There is no touch in docked but games still allow it
334 if (Settings::values.use_docked_mode.GetValue()) { 334 if (Settings::IsDockedMode()) {
335 gesture.points[id] = { 335 gesture.points[id] = {
336 .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), 336 .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width),
337 .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), 337 .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height),
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 28818c813..3b349b4c4 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -1518,7 +1518,7 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1518 return false; 1518 return false;
1519 } 1519 }
1520 // Handheld shouldn't be supported in docked mode 1520 // Handheld shouldn't be supported in docked mode
1521 if (Settings::values.use_docked_mode.GetValue()) { 1521 if (Settings::IsDockedMode()) {
1522 return false; 1522 return false;
1523 } 1523 }
1524 1524
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index 65c11a2f3..3b83c5ed7 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -7,17 +7,16 @@
7#include "core/hle/service/ipc_helpers.h" 7#include "core/hle/service/ipc_helpers.h"
8#include "core/hle/service/mii/mii.h" 8#include "core/hle/service/mii/mii.h"
9#include "core/hle/service/mii/mii_manager.h" 9#include "core/hle/service/mii/mii_manager.h"
10#include "core/hle/service/mii/mii_result.h"
10#include "core/hle/service/server_manager.h" 11#include "core/hle/service/server_manager.h"
11#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
12 13
13namespace Service::Mii { 14namespace Service::Mii {
14 15
15constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
16
17class IDatabaseService final : public ServiceFramework<IDatabaseService> { 16class IDatabaseService final : public ServiceFramework<IDatabaseService> {
18public: 17public:
19 explicit IDatabaseService(Core::System& system_) 18 explicit IDatabaseService(Core::System& system_, bool is_system_)
20 : ServiceFramework{system_, "IDatabaseService"} { 19 : ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} {
21 // clang-format off 20 // clang-format off
22 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
23 {0, &IDatabaseService::IsUpdated, "IsUpdated"}, 22 {0, &IDatabaseService::IsUpdated, "IsUpdated"},
@@ -54,34 +53,27 @@ public:
54 } 53 }
55 54
56private: 55private:
57 template <typename T>
58 std::vector<u8> SerializeArray(const std::vector<T>& values) {
59 std::vector<u8> out(values.size() * sizeof(T));
60 std::size_t offset{};
61 for (const auto& value : values) {
62 std::memcpy(out.data() + offset, &value, sizeof(T));
63 offset += sizeof(T);
64 }
65 return out;
66 }
67
68 void IsUpdated(HLERequestContext& ctx) { 56 void IsUpdated(HLERequestContext& ctx) {
69 IPC::RequestParser rp{ctx}; 57 IPC::RequestParser rp{ctx};
70 const auto source_flag{rp.PopRaw<SourceFlag>()}; 58 const auto source_flag{rp.PopRaw<SourceFlag>()};
71 59
72 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); 60 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
73 61
62 const bool is_updated = manager.IsUpdated(metadata, source_flag);
63
74 IPC::ResponseBuilder rb{ctx, 3}; 64 IPC::ResponseBuilder rb{ctx, 3};
75 rb.Push(ResultSuccess); 65 rb.Push(ResultSuccess);
76 rb.Push(manager.CheckAndResetUpdateCounter(source_flag, current_update_counter)); 66 rb.Push<u8>(is_updated);
77 } 67 }
78 68
79 void IsFullDatabase(HLERequestContext& ctx) { 69 void IsFullDatabase(HLERequestContext& ctx) {
80 LOG_DEBUG(Service_Mii, "called"); 70 LOG_DEBUG(Service_Mii, "called");
81 71
72 const bool is_full_database = manager.IsFullDatabase();
73
82 IPC::ResponseBuilder rb{ctx, 3}; 74 IPC::ResponseBuilder rb{ctx, 3};
83 rb.Push(ResultSuccess); 75 rb.Push(ResultSuccess);
84 rb.Push(manager.IsFullDatabase()); 76 rb.Push<u8>(is_full_database);
85 } 77 }
86 78
87 void GetCount(HLERequestContext& ctx) { 79 void GetCount(HLERequestContext& ctx) {
@@ -90,57 +82,63 @@ private:
90 82
91 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); 83 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
92 84
85 const u32 mii_count = manager.GetCount(metadata, source_flag);
86
93 IPC::ResponseBuilder rb{ctx, 3}; 87 IPC::ResponseBuilder rb{ctx, 3};
94 rb.Push(ResultSuccess); 88 rb.Push(ResultSuccess);
95 rb.Push<u32>(manager.GetCount(source_flag)); 89 rb.Push(mii_count);
96 } 90 }
97 91
98 void Get(HLERequestContext& ctx) { 92 void Get(HLERequestContext& ctx) {
99 IPC::RequestParser rp{ctx}; 93 IPC::RequestParser rp{ctx};
100 const auto source_flag{rp.PopRaw<SourceFlag>()}; 94 const auto source_flag{rp.PopRaw<SourceFlag>()};
95 const auto output_size{ctx.GetWriteBufferNumElements<CharInfoElement>()};
101 96
102 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); 97 LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
98
99 u32 mii_count{};
100 std::vector<CharInfoElement> char_info_elements(output_size);
101 Result result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
103 102
104 const auto default_miis{manager.GetDefault(source_flag)}; 103 if (mii_count != 0) {
105 if (default_miis.size() > 0) { 104 ctx.WriteBuffer(char_info_elements);
106 ctx.WriteBuffer(SerializeArray(default_miis));
107 } 105 }
108 106
109 IPC::ResponseBuilder rb{ctx, 3}; 107 IPC::ResponseBuilder rb{ctx, 3};
110 rb.Push(ResultSuccess); 108 rb.Push(result);
111 rb.Push<u32>(static_cast<u32>(default_miis.size())); 109 rb.Push(mii_count);
112 } 110 }
113 111
114 void Get1(HLERequestContext& ctx) { 112 void Get1(HLERequestContext& ctx) {
115 IPC::RequestParser rp{ctx}; 113 IPC::RequestParser rp{ctx};
116 const auto source_flag{rp.PopRaw<SourceFlag>()}; 114 const auto source_flag{rp.PopRaw<SourceFlag>()};
115 const auto output_size{ctx.GetWriteBufferNumElements<CharInfo>()};
117 116
118 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); 117 LOG_DEBUG(Service_Mii, "called with source_flag={}, out_size={}", source_flag, output_size);
119 118
120 const auto default_miis{manager.GetDefault(source_flag)}; 119 u32 mii_count{};
120 std::vector<CharInfo> char_info(output_size);
121 Result result = manager.Get(metadata, char_info, mii_count, source_flag);
121 122
122 std::vector<CharInfo> values; 123 if (mii_count != 0) {
123 for (const auto& element : default_miis) { 124 ctx.WriteBuffer(char_info);
124 values.emplace_back(element.info);
125 } 125 }
126 126
127 ctx.WriteBuffer(SerializeArray(values));
128
129 IPC::ResponseBuilder rb{ctx, 3}; 127 IPC::ResponseBuilder rb{ctx, 3};
130 rb.Push(ResultSuccess); 128 rb.Push(result);
131 rb.Push<u32>(static_cast<u32>(default_miis.size())); 129 rb.Push(mii_count);
132 } 130 }
133 131
134 void UpdateLatest(HLERequestContext& ctx) { 132 void UpdateLatest(HLERequestContext& ctx) {
135 IPC::RequestParser rp{ctx}; 133 IPC::RequestParser rp{ctx};
136 const auto info{rp.PopRaw<CharInfo>()}; 134 const auto char_info{rp.PopRaw<CharInfo>()};
137 const auto source_flag{rp.PopRaw<SourceFlag>()}; 135 const auto source_flag{rp.PopRaw<SourceFlag>()};
138 136
139 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag); 137 LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
140 138
141 CharInfo new_char_info{}; 139 CharInfo new_char_info{};
142 const auto result{manager.UpdateLatest(&new_char_info, info, source_flag)}; 140 const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
143 if (result != ResultSuccess) { 141 if (result.IsFailure()) {
144 IPC::ResponseBuilder rb{ctx, 2}; 142 IPC::ResponseBuilder rb{ctx, 2};
145 rb.Push(result); 143 rb.Push(result);
146 return; 144 return;
@@ -153,7 +151,6 @@ private:
153 151
154 void BuildRandom(HLERequestContext& ctx) { 152 void BuildRandom(HLERequestContext& ctx) {
155 IPC::RequestParser rp{ctx}; 153 IPC::RequestParser rp{ctx};
156
157 const auto age{rp.PopRaw<Age>()}; 154 const auto age{rp.PopRaw<Age>()};
158 const auto gender{rp.PopRaw<Gender>()}; 155 const auto gender{rp.PopRaw<Gender>()};
159 const auto race{rp.PopRaw<Race>()}; 156 const auto race{rp.PopRaw<Race>()};
@@ -162,47 +159,48 @@ private:
162 159
163 if (age > Age::All) { 160 if (age > Age::All) {
164 IPC::ResponseBuilder rb{ctx, 2}; 161 IPC::ResponseBuilder rb{ctx, 2};
165 rb.Push(ERROR_INVALID_ARGUMENT); 162 rb.Push(ResultInvalidArgument);
166 LOG_ERROR(Service_Mii, "invalid age={}", age);
167 return; 163 return;
168 } 164 }
169 165
170 if (gender > Gender::All) { 166 if (gender > Gender::All) {
171 IPC::ResponseBuilder rb{ctx, 2}; 167 IPC::ResponseBuilder rb{ctx, 2};
172 rb.Push(ERROR_INVALID_ARGUMENT); 168 rb.Push(ResultInvalidArgument);
173 LOG_ERROR(Service_Mii, "invalid gender={}", gender);
174 return; 169 return;
175 } 170 }
176 171
177 if (race > Race::All) { 172 if (race > Race::All) {
178 IPC::ResponseBuilder rb{ctx, 2}; 173 IPC::ResponseBuilder rb{ctx, 2};
179 rb.Push(ERROR_INVALID_ARGUMENT); 174 rb.Push(ResultInvalidArgument);
180 LOG_ERROR(Service_Mii, "invalid race={}", race);
181 return; 175 return;
182 } 176 }
183 177
178 CharInfo char_info{};
179 manager.BuildRandom(char_info, age, gender, race);
180
184 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; 181 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
185 rb.Push(ResultSuccess); 182 rb.Push(ResultSuccess);
186 rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race)); 183 rb.PushRaw<CharInfo>(char_info);
187 } 184 }
188 185
189 void BuildDefault(HLERequestContext& ctx) { 186 void BuildDefault(HLERequestContext& ctx) {
190 IPC::RequestParser rp{ctx}; 187 IPC::RequestParser rp{ctx};
191 const auto index{rp.Pop<u32>()}; 188 const auto index{rp.Pop<u32>()};
192 189
193 LOG_DEBUG(Service_Mii, "called with index={}", index); 190 LOG_INFO(Service_Mii, "called with index={}", index);
194 191
195 if (index > 5) { 192 if (index > 5) {
196 LOG_ERROR(Service_Mii, "invalid argument, index cannot be greater than 5 but is {:08X}",
197 index);
198 IPC::ResponseBuilder rb{ctx, 2}; 193 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(ERROR_INVALID_ARGUMENT); 194 rb.Push(ResultInvalidArgument);
200 return; 195 return;
201 } 196 }
202 197
198 CharInfo char_info{};
199 manager.BuildDefault(char_info, index);
200
203 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; 201 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
204 rb.Push(ResultSuccess); 202 rb.Push(ResultSuccess);
205 rb.PushRaw<CharInfo>(manager.BuildDefault(index)); 203 rb.PushRaw<CharInfo>(char_info);
206 } 204 }
207 205
208 void GetIndex(HLERequestContext& ctx) { 206 void GetIndex(HLERequestContext& ctx) {
@@ -211,19 +209,21 @@ private:
211 209
212 LOG_DEBUG(Service_Mii, "called"); 210 LOG_DEBUG(Service_Mii, "called");
213 211
214 u32 index{}; 212 s32 index{};
213 const auto result = manager.GetIndex(metadata, info, index);
214
215 IPC::ResponseBuilder rb{ctx, 3}; 215 IPC::ResponseBuilder rb{ctx, 3};
216 rb.Push(manager.GetIndex(info, index)); 216 rb.Push(result);
217 rb.Push(index); 217 rb.Push(index);
218 } 218 }
219 219
220 void SetInterfaceVersion(HLERequestContext& ctx) { 220 void SetInterfaceVersion(HLERequestContext& ctx) {
221 IPC::RequestParser rp{ctx}; 221 IPC::RequestParser rp{ctx};
222 current_interface_version = rp.PopRaw<u32>(); 222 const auto interface_version{rp.PopRaw<u32>()};
223 223
224 LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version); 224 LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);
225 225
226 UNIMPLEMENTED_IF(current_interface_version != 1); 226 manager.SetInterfaceVersion(metadata, interface_version);
227 227
228 IPC::ResponseBuilder rb{ctx, 2}; 228 IPC::ResponseBuilder rb{ctx, 2};
229 rb.Push(ResultSuccess); 229 rb.Push(ResultSuccess);
@@ -231,30 +231,27 @@ private:
231 231
232 void Convert(HLERequestContext& ctx) { 232 void Convert(HLERequestContext& ctx) {
233 IPC::RequestParser rp{ctx}; 233 IPC::RequestParser rp{ctx};
234
235 const auto mii_v3{rp.PopRaw<Ver3StoreData>()}; 234 const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
236 235
237 LOG_INFO(Service_Mii, "called"); 236 LOG_INFO(Service_Mii, "called");
238 237
238 CharInfo char_info{};
239 manager.ConvertV3ToCharInfo(char_info, mii_v3);
240
239 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)}; 241 IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
240 rb.Push(ResultSuccess); 242 rb.Push(ResultSuccess);
241 rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3)); 243 rb.PushRaw<CharInfo>(char_info);
242 }
243
244 constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
245 return current_interface_version >= interface_version;
246 } 244 }
247 245
248 MiiManager manager; 246 MiiManager manager{};
249 247 DatabaseSessionMetadata metadata{};
250 u32 current_interface_version{}; 248 bool is_system{};
251 u64 current_update_counter{};
252}; 249};
253 250
254class MiiDBModule final : public ServiceFramework<MiiDBModule> { 251class MiiDBModule final : public ServiceFramework<MiiDBModule> {
255public: 252public:
256 explicit MiiDBModule(Core::System& system_, const char* name_) 253 explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_)
257 : ServiceFramework{system_, name_} { 254 : ServiceFramework{system_, name_}, is_system{is_system_} {
258 // clang-format off 255 // clang-format off
259 static const FunctionInfo functions[] = { 256 static const FunctionInfo functions[] = {
260 {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, 257 {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
@@ -268,10 +265,12 @@ private:
268 void GetDatabaseService(HLERequestContext& ctx) { 265 void GetDatabaseService(HLERequestContext& ctx) {
269 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 266 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
270 rb.Push(ResultSuccess); 267 rb.Push(ResultSuccess);
271 rb.PushIpcInterface<IDatabaseService>(system); 268 rb.PushIpcInterface<IDatabaseService>(system, is_system);
272 269
273 LOG_DEBUG(Service_Mii, "called"); 270 LOG_DEBUG(Service_Mii, "called");
274 } 271 }
272
273 bool is_system{};
275}; 274};
276 275
277class MiiImg final : public ServiceFramework<MiiImg> { 276class MiiImg final : public ServiceFramework<MiiImg> {
@@ -303,8 +302,10 @@ public:
303void LoopProcess(Core::System& system) { 302void LoopProcess(Core::System& system) {
304 auto server_manager = std::make_unique<ServerManager>(system); 303 auto server_manager = std::make_unique<ServerManager>(system);
305 304
306 server_manager->RegisterNamedService("mii:e", std::make_shared<MiiDBModule>(system, "mii:e")); 305 server_manager->RegisterNamedService("mii:e",
307 server_manager->RegisterNamedService("mii:u", std::make_shared<MiiDBModule>(system, "mii:u")); 306 std::make_shared<MiiDBModule>(system, "mii:e", true));
307 server_manager->RegisterNamedService("mii:u",
308 std::make_shared<MiiDBModule>(system, "mii:u", false));
308 server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system)); 309 server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system));
309 ServerManager::RunServer(std::move(server_manager)); 310 ServerManager::RunServer(std::move(server_manager));
310} 311}
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 46125d473..292d63777 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -10,386 +10,24 @@
10 10
11#include "core/hle/service/acc/profile_manager.h" 11#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/mii/mii_manager.h" 12#include "core/hle/service/mii/mii_manager.h"
13#include "core/hle/service/mii/raw_data.h" 13#include "core/hle/service/mii/mii_result.h"
14#include "core/hle/service/mii/mii_util.h"
15#include "core/hle/service/mii/types/core_data.h"
16#include "core/hle/service/mii/types/raw_data.h"
14 17
15namespace Service::Mii { 18namespace Service::Mii {
16
17namespace {
18
19constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
20
21constexpr std::size_t BaseMiiCount{2};
22constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; 19constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
23 20
24constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; 21MiiManager::MiiManager() {}
25constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7};
26constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13};
27constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23};
28constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0};
29constexpr std::array<u8, 62> EyeRotateLookup{
30 {0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
31 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
32 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
33 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}};
34constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07,
35 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06,
36 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}};
37
38template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize>
39std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) {
40 std::array<T, DestArraySize> out{};
41 std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize));
42 return out;
43}
44
45CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
46 MiiStoreBitFields bf;
47 std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
48
49 return {
50 .uuid = data.data.uuid,
51 .name = ResizeArray<char16_t, 10, 11>(data.data.name),
52 .font_region = static_cast<u8>(bf.font_region.Value()),
53 .favorite_color = static_cast<u8>(bf.favorite_color.Value()),
54 .gender = static_cast<u8>(bf.gender.Value()),
55 .height = static_cast<u8>(bf.height.Value()),
56 .build = static_cast<u8>(bf.build.Value()),
57 .type = static_cast<u8>(bf.type.Value()),
58 .region_move = static_cast<u8>(bf.region_move.Value()),
59 .faceline_type = static_cast<u8>(bf.faceline_type.Value()),
60 .faceline_color = static_cast<u8>(bf.faceline_color.Value()),
61 .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()),
62 .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()),
63 .hair_type = static_cast<u8>(bf.hair_type.Value()),
64 .hair_color = static_cast<u8>(bf.hair_color.Value()),
65 .hair_flip = static_cast<u8>(bf.hair_flip.Value()),
66 .eye_type = static_cast<u8>(bf.eye_type.Value()),
67 .eye_color = static_cast<u8>(bf.eye_color.Value()),
68 .eye_scale = static_cast<u8>(bf.eye_scale.Value()),
69 .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()),
70 .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()),
71 .eye_x = static_cast<u8>(bf.eye_x.Value()),
72 .eye_y = static_cast<u8>(bf.eye_y.Value()),
73 .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()),
74 .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()),
75 .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()),
76 .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()),
77 .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()),
78 .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()),
79 .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3),
80 .nose_type = static_cast<u8>(bf.nose_type.Value()),
81 .nose_scale = static_cast<u8>(bf.nose_scale.Value()),
82 .nose_y = static_cast<u8>(bf.nose_y.Value()),
83 .mouth_type = static_cast<u8>(bf.mouth_type.Value()),
84 .mouth_color = static_cast<u8>(bf.mouth_color.Value()),
85 .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()),
86 .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()),
87 .mouth_y = static_cast<u8>(bf.mouth_y.Value()),
88 .beard_color = static_cast<u8>(bf.beard_color.Value()),
89 .beard_type = static_cast<u8>(bf.beard_type.Value()),
90 .mustache_type = static_cast<u8>(bf.mustache_type.Value()),
91 .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()),
92 .mustache_y = static_cast<u8>(bf.mustache_y.Value()),
93 .glasses_type = static_cast<u8>(bf.glasses_type.Value()),
94 .glasses_color = static_cast<u8>(bf.glasses_color.Value()),
95 .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()),
96 .glasses_y = static_cast<u8>(bf.glasses_y.Value()),
97 .mole_type = static_cast<u8>(bf.mole_type.Value()),
98 .mole_scale = static_cast<u8>(bf.mole_scale.Value()),
99 .mole_x = static_cast<u8>(bf.mole_x.Value()),
100 .mole_y = static_cast<u8>(bf.mole_y.Value()),
101 .padding = 0,
102 };
103}
104
105u16 GenerateCrc16(const void* data, std::size_t size) {
106 s32 crc{};
107 for (std::size_t i = 0; i < size; i++) {
108 crc ^= static_cast<const u8*>(data)[i] << 8;
109 for (std::size_t j = 0; j < 8; j++) {
110 crc <<= 1;
111 if ((crc & 0x10000) != 0) {
112 crc = (crc ^ 0x1021) & 0xFFFF;
113 }
114 }
115 }
116 return Common::swap16(static_cast<u16>(crc));
117}
118
119template <typename T>
120T GetRandomValue(T min, T max) {
121 std::random_device device;
122 std::mt19937 gen(device());
123 std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max));
124 return static_cast<T>(distribution(gen));
125}
126
127template <typename T>
128T GetRandomValue(T max) {
129 return GetRandomValue<T>({}, max);
130}
131
132MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) {
133 MiiStoreBitFields bf{};
134
135 if (gender == Gender::All) {
136 gender = GetRandomValue<Gender>(Gender::Maximum);
137 }
138
139 bf.gender.Assign(gender);
140 bf.favorite_color.Assign(GetRandomValue<u8>(11));
141 bf.region_move.Assign(0);
142 bf.font_region.Assign(FontRegion::Standard);
143 bf.type.Assign(0);
144 bf.height.Assign(64);
145 bf.build.Assign(64);
146
147 if (age == Age::All) {
148 const auto temp{GetRandomValue<int>(10)};
149 if (temp >= 8) {
150 age = Age::Old;
151 } else if (temp >= 4) {
152 age = Age::Normal;
153 } else {
154 age = Age::Young;
155 }
156 }
157
158 if (race == Race::All) {
159 const auto temp{GetRandomValue<int>(10)};
160 if (temp >= 8) {
161 race = Race::Black;
162 } else if (temp >= 4) {
163 race = Race::White;
164 } else {
165 race = Race::Asian;
166 }
167 }
168
169 u32 axis_y{};
170 if (gender == Gender::Female && age == Age::Young) {
171 axis_y = GetRandomValue<u32>(3);
172 }
173
174 const std::size_t index{3 * static_cast<std::size_t>(age) +
175 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
176
177 const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)};
178 const auto faceline_color_info{RawData::RandomMiiFacelineColor.at(
179 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
180 const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
181 const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
182 const auto hair_type_info{RawData::RandomMiiHairType.at(index)};
183 const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
184 static_cast<std::size_t>(age))};
185 const auto eye_type_info{RawData::RandomMiiEyeType.at(index)};
186 const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
187 const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
188 const auto nose_type_info{RawData::RandomMiiNoseType.at(index)};
189 const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)};
190 const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
191
192 bf.faceline_type.Assign(
193 faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
194 bf.faceline_color.Assign(
195 faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
196 bf.faceline_wrinkle.Assign(
197 faceline_wrinkle_info
198 .values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
199 bf.faceline_makeup.Assign(
200 faceline_makeup_info
201 .values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
202
203 bf.hair_type.Assign(
204 hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]);
205 bf.hair_color.Assign(
206 HairColorLookup[hair_color_info
207 .values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]);
208 bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum));
209
210 bf.eye_type.Assign(
211 eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]);
212
213 const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
214 const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
215 const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
216 const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]};
217
218 bf.eye_color.Assign(
219 EyeColorLookup[eye_color_info
220 .values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]);
221 bf.eye_scale.Assign(4);
222 bf.eye_aspect.Assign(3);
223 bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate);
224 bf.eye_x.Assign(2);
225 bf.eye_y.Assign(axis_y + 12);
226
227 bf.eyebrow_type.Assign(
228 eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
229
230 const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
231 const auto eyebrow_y{race == Race::Asian ? 9 : 10};
232 const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6};
233 const auto eyebrow_rotate{
234 32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]};
235
236 bf.eyebrow_color.Assign(bf.hair_color);
237 bf.eyebrow_scale.Assign(4);
238 bf.eyebrow_aspect.Assign(3);
239 bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate);
240 bf.eyebrow_x.Assign(2);
241 bf.eyebrow_y.Assign(axis_y + eyebrow_y);
242
243 const auto nose_scale{gender == Gender::Female ? 3 : 4};
244
245 bf.nose_type.Assign(
246 nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]);
247 bf.nose_scale.Assign(nose_scale);
248 bf.nose_y.Assign(axis_y + 9);
249
250 const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0};
251
252 bf.mouth_type.Assign(
253 mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
254 bf.mouth_color.Assign(MouthColorLookup[mouth_color]);
255 bf.mouth_scale.Assign(4);
256 bf.mouth_aspect.Assign(3);
257 bf.mouth_y.Assign(axis_y + 13);
258
259 bf.beard_color.Assign(bf.hair_color);
260 bf.mustache_scale.Assign(4);
261
262 if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) {
263 const auto mustache_and_beard_flag{
264 GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)};
265
266 auto beard_type{BeardType::None};
267 auto mustache_type{MustacheType::None};
268
269 if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
270 BeardAndMustacheFlag::Beard) {
271 beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5);
272 }
273
274 if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
275 BeardAndMustacheFlag::Mustache) {
276 mustache_type =
277 GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5);
278 }
279
280 bf.mustache_type.Assign(mustache_type);
281 bf.beard_type.Assign(beard_type);
282 bf.mustache_y.Assign(10);
283 } else {
284 bf.mustache_type.Assign(MustacheType::None);
285 bf.beard_type.Assign(BeardType::None);
286 bf.mustache_y.Assign(axis_y + 10);
287 }
288
289 const auto glasses_type_start{GetRandomValue<std::size_t>(100)};
290 u8 glasses_type{};
291 while (glasses_type_start < glasses_type_info.values[glasses_type]) {
292 if (++glasses_type >= glasses_type_info.values_count) {
293 ASSERT(false);
294 break;
295 }
296 }
297
298 bf.glasses_type.Assign(glasses_type);
299 bf.glasses_color.Assign(GlassesColorLookup[0]);
300 bf.glasses_scale.Assign(4);
301 bf.glasses_y.Assign(axis_y + 10);
302
303 bf.mole_type.Assign(0);
304 bf.mole_scale.Assign(4);
305 bf.mole_x.Assign(2);
306 bf.mole_y.Assign(20);
307
308 return {DefaultMiiName, bf, user_id};
309}
310
311MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) {
312 MiiStoreBitFields bf{};
313
314 bf.font_region.Assign(info.font_region);
315 bf.favorite_color.Assign(info.favorite_color);
316 bf.gender.Assign(info.gender);
317 bf.height.Assign(info.height);
318 bf.build.Assign(info.weight);
319 bf.type.Assign(info.type);
320 bf.region_move.Assign(info.region);
321 bf.faceline_type.Assign(info.face_type);
322 bf.faceline_color.Assign(info.face_color);
323 bf.faceline_wrinkle.Assign(info.face_wrinkle);
324 bf.faceline_makeup.Assign(info.face_makeup);
325 bf.hair_type.Assign(info.hair_type);
326 bf.hair_color.Assign(HairColorLookup[info.hair_color]);
327 bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip));
328 bf.eye_type.Assign(info.eye_type);
329 bf.eye_color.Assign(EyeColorLookup[info.eye_color]);
330 bf.eye_scale.Assign(info.eye_scale);
331 bf.eye_aspect.Assign(info.eye_aspect);
332 bf.eye_rotate.Assign(info.eye_rotate);
333 bf.eye_x.Assign(info.eye_x);
334 bf.eye_y.Assign(info.eye_y);
335 bf.eyebrow_type.Assign(info.eyebrow_type);
336 bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]);
337 bf.eyebrow_scale.Assign(info.eyebrow_scale);
338 bf.eyebrow_aspect.Assign(info.eyebrow_aspect);
339 bf.eyebrow_rotate.Assign(info.eyebrow_rotate);
340 bf.eyebrow_x.Assign(info.eyebrow_x);
341 bf.eyebrow_y.Assign(info.eyebrow_y - 3);
342 bf.nose_type.Assign(info.nose_type);
343 bf.nose_scale.Assign(info.nose_scale);
344 bf.nose_y.Assign(info.nose_y);
345 bf.mouth_type.Assign(info.mouth_type);
346 bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]);
347 bf.mouth_scale.Assign(info.mouth_scale);
348 bf.mouth_aspect.Assign(info.mouth_aspect);
349 bf.mouth_y.Assign(info.mouth_y);
350 bf.beard_color.Assign(HairColorLookup[info.beard_color]);
351 bf.beard_type.Assign(static_cast<BeardType>(info.beard_type));
352 bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type));
353 bf.mustache_scale.Assign(info.mustache_scale);
354 bf.mustache_y.Assign(info.mustache_y);
355 bf.glasses_type.Assign(info.glasses_type);
356 bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]);
357 bf.glasses_scale.Assign(info.glasses_scale);
358 bf.glasses_y.Assign(info.glasses_y);
359 bf.mole_type.Assign(info.mole_type);
360 bf.mole_scale.Assign(info.mole_scale);
361 bf.mole_x.Assign(info.mole_x);
362 bf.mole_y.Assign(info.mole_y);
363
364 return {DefaultMiiName, bf, user_id};
365}
366
367} // namespace
368
369MiiStoreData::MiiStoreData() = default;
370
371MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields,
372 const Common::UUID& user_id) {
373 data.name = name;
374 data.uuid = Common::UUID::MakeRandomRFC4122V4();
375
376 std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields));
377 data_crc = GenerateCrc16(data.data.data(), sizeof(data));
378 device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID));
379}
380 22
381MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {} 23bool MiiManager::IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
382
383bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) {
384 if ((source_flag & SourceFlag::Database) == SourceFlag::None) { 24 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
385 return false; 25 return false;
386 } 26 }
387 27
388 const bool result{current_update_counter != update_counter}; 28 const auto metadata_update_counter = metadata.update_counter;
389 29 metadata.update_counter = update_counter;
390 current_update_counter = update_counter; 30 return metadata_update_counter != update_counter;
391
392 return result;
393} 31}
394 32
395bool MiiManager::IsFullDatabase() const { 33bool MiiManager::IsFullDatabase() const {
@@ -397,301 +35,138 @@ bool MiiManager::IsFullDatabase() const {
397 return false; 35 return false;
398} 36}
399 37
400u32 MiiManager::GetCount(SourceFlag source_flag) const { 38u32 MiiManager::GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const {
401 std::size_t count{}; 39 u32 mii_count{};
40 if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
41 mii_count += DefaultMiiCount;
42 }
402 if ((source_flag & SourceFlag::Database) != SourceFlag::None) { 43 if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
403 // TODO(bunnei): We don't implement the Mii database, but when we do, update this 44 // TODO(bunnei): We don't implement the Mii database, but when we do, update this
404 count += 0;
405 } 45 }
406 if ((source_flag & SourceFlag::Default) != SourceFlag::None) { 46 return mii_count;
407 count += (DefaultMiiCount - BaseMiiCount);
408 }
409 return static_cast<u32>(count);
410} 47}
411 48
412Result MiiManager::UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag) { 49Result MiiManager::UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
50 const CharInfo& char_info, SourceFlag source_flag) {
413 if ((source_flag & SourceFlag::Database) == SourceFlag::None) { 51 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
414 return ERROR_CANNOT_FIND_ENTRY; 52 return ResultNotFound;
415 } 53 }
416 54
417 // TODO(bunnei): We don't implement the Mii database, so we can't have an entry 55 // TODO(bunnei): We don't implement the Mii database, so we can't have an entry
418 return ERROR_CANNOT_FIND_ENTRY; 56 return ResultNotFound;
419} 57}
420 58
421CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { 59void MiiManager::BuildDefault(CharInfo& out_char_info, u32 index) const {
422 return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); 60 StoreData store_data{};
61 store_data.BuildDefault(index);
62 out_char_info.SetFromStoreData(store_data);
423} 63}
424 64
425CharInfo MiiManager::BuildDefault(std::size_t index) { 65void MiiManager::BuildBase(CharInfo& out_char_info, Gender gender) const {
426 return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); 66 StoreData store_data{};
67 store_data.BuildBase(gender);
68 out_char_info.SetFromStoreData(store_data);
427} 69}
428 70
429CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const { 71void MiiManager::BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const {
430 Service::Mii::MiiManager manager; 72 StoreData store_data{};
431 auto mii = manager.BuildDefault(0); 73 store_data.BuildRandom(age, gender, race);
432 74 out_char_info.SetFromStoreData(store_data);
433 if (!ValidateV3Info(mii_v3)) {
434 return mii;
435 }
436
437 // TODO: We are ignoring a bunch of data from the mii_v3
438
439 mii.gender = static_cast<u8>(mii_v3.mii_information.gender);
440 mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color);
441 mii.height = mii_v3.height;
442 mii.build = mii_v3.build;
443
444 // Copy name until string terminator
445 mii.name = {};
446 for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
447 mii.name[index] = mii_v3.mii_name[index];
448 if (mii.name[index] == 0) {
449 break;
450 }
451 }
452
453 mii.font_region = mii_v3.region_information.character_set;
454
455 mii.faceline_type = mii_v3.appearance_bits1.face_shape;
456 mii.faceline_color = mii_v3.appearance_bits1.skin_color;
457 mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles;
458 mii.faceline_make = mii_v3.appearance_bits2.makeup;
459
460 mii.hair_type = mii_v3.hair_style;
461 mii.hair_color = mii_v3.appearance_bits3.hair_color;
462 mii.hair_flip = mii_v3.appearance_bits3.flip_hair;
463
464 mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type);
465 mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color);
466 mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale);
467 mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch);
468 mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation);
469 mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing);
470 mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position);
471
472 mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style);
473 mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color);
474 mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale);
475 mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale);
476 mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation);
477 mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing);
478 mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position);
479
480 mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type);
481 mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale);
482 mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position);
483
484 mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type);
485 mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color);
486 mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale);
487 mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch);
488 mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position);
489
490 mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type);
491 mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale);
492 mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position);
493
494 mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type);
495 mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color);
496
497 mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type);
498 mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color);
499 mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale);
500 mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position);
501
502 mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled);
503 mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale);
504 mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position);
505 mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position);
506
507 // TODO: Validate mii data
508
509 return mii;
510} 75}
511 76
512Ver3StoreData MiiManager::BuildFromStoreData(const CharInfo& mii) const { 77void MiiManager::ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const {
513 Service::Mii::MiiManager manager; 78 StoreData store_data{};
514 Ver3StoreData mii_v3{}; 79 mii_v3.BuildToStoreData(store_data);
515 80 out_char_info.SetFromStoreData(store_data);
516 // TODO: We are ignoring a bunch of data from the mii_v3 81}
517
518 mii_v3.version = 1;
519 mii_v3.mii_information.gender.Assign(mii.gender);
520 mii_v3.mii_information.favorite_color.Assign(mii.favorite_color);
521 mii_v3.height = mii.height;
522 mii_v3.build = mii.build;
523 82
524 // Copy name until string terminator 83Result MiiManager::Get(const DatabaseSessionMetadata& metadata,
525 mii_v3.mii_name = {}; 84 std::span<CharInfoElement> out_elements, u32& out_count,
526 for (std::size_t index = 0; index < mii.name.size() - 1; index++) { 85 SourceFlag source_flag) {
527 mii_v3.mii_name[index] = mii.name[index]; 86 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
528 if (mii_v3.mii_name[index] == 0) { 87 return BuildDefault(out_elements, out_count, source_flag);
529 break;
530 }
531 } 88 }
532 89
533 mii_v3.region_information.character_set.Assign(mii.font_region); 90 // TODO(bunnei): We don't implement the Mii database, so we can't have an entry
534 91
535 mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); 92 // Include default Mii at the end of the list
536 mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); 93 return BuildDefault(out_elements, out_count, source_flag);
537 mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); 94}
538 95
539 mii_v3.hair_style = mii.hair_type; 96Result MiiManager::Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
540 mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); 97 u32& out_count, SourceFlag source_flag) {
98 if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
99 return BuildDefault(out_char_info, out_count, source_flag);
100 }
541 101
542 mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); 102 // TODO(bunnei): We don't implement the Mii database, so we can't have an entry
543 mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
544 mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
545 mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
546 mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x);
547 mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
548 103
549 mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); 104 // Include default Mii at the end of the list
550 mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); 105 return BuildDefault(out_char_info, out_count, source_flag);
551 mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); 106}
552 mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
553 mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x);
554 mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y);
555 107
556 mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); 108Result MiiManager::BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
557 mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); 109 SourceFlag source_flag) {
558 mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); 110 if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
111 return ResultSuccess;
112 }
559 113
560 mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); 114 StoreData store_data{};
561 mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
562 mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
563 mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
564 115
565 mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); 116 for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
566 mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); 117 if (out_elements.size() <= static_cast<std::size_t>(out_count)) {
567 mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); 118 return ResultInvalidArgumentSize;
119 }
568 120
569 mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); 121 store_data.BuildDefault(static_cast<u32>(index));
570 122
571 mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); 123 out_elements[out_count].source = Source::Default;
572 mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); 124 out_elements[out_count].char_info.SetFromStoreData(store_data);
125 out_count++;
126 }
573 127
574 mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); 128 return ResultSuccess;
575 mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); 129}
576 mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
577 mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
578 130
579 // These types are converted to V3 from a table 131Result MiiManager::BuildDefault(std::span<CharInfo> out_char_info, u32& out_count,
580 mii_v3.appearance_bits1.skin_color.Assign(Ver3FacelineColorTable[mii.faceline_color]); 132 SourceFlag source_flag) {
581 mii_v3.appearance_bits3.hair_color.Assign(Ver3HairColorTable[mii.hair_color]); 133 if ((source_flag & SourceFlag::Default) == SourceFlag::None) {
582 mii_v3.appearance_bits4.eye_color.Assign(Ver3EyeColorTable[mii.eye_color]); 134 return ResultSuccess;
583 mii_v3.appearance_bits5.eyebrow_color.Assign(Ver3HairColorTable[mii.eyebrow_color]); 135 }
584 mii_v3.appearance_bits7.mouth_color.Assign(Ver3MouthlineColorTable[mii.mouth_color]);
585 mii_v3.appearance_bits9.facial_hair_color.Assign(Ver3HairColorTable[mii.beard_color]);
586 mii_v3.appearance_bits10.glasses_color.Assign(Ver3GlassColorTable[mii.glasses_color]);
587 mii_v3.appearance_bits10.glasses_type.Assign(Ver3GlassTypeTable[mii.glasses_type]);
588 136
589 mii_v3.crc = GenerateCrc16(&mii_v3, sizeof(Ver3StoreData) - sizeof(u16)); 137 StoreData store_data{};
590 138
591 // TODO: Validate mii_v3 data 139 for (std::size_t index = 0; index < DefaultMiiCount; ++index) {
140 if (out_char_info.size() <= static_cast<std::size_t>(out_count)) {
141 return ResultInvalidArgumentSize;
142 }
592 143
593 return mii_v3; 144 store_data.BuildDefault(static_cast<u32>(index));
594}
595 145
596NfpStoreDataExtension MiiManager::SetFromStoreData(const CharInfo& mii) const { 146 out_char_info[out_count].SetFromStoreData(store_data);
597 return { 147 out_count++;
598 .faceline_color = static_cast<u8>(mii.faceline_color & 0xf), 148 }
599 .hair_color = static_cast<u8>(mii.hair_color & 0x7f),
600 .eye_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
601 .eyebrow_color = static_cast<u8>(mii.eyebrow_color & 0x7f),
602 .mouth_color = static_cast<u8>(mii.mouth_color & 0x7f),
603 .beard_color = static_cast<u8>(mii.beard_color & 0x7f),
604 .glass_color = static_cast<u8>(mii.glasses_color & 0x7f),
605 .glass_type = static_cast<u8>(mii.glasses_type & 0x1f),
606 };
607}
608 149
609bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const { 150 return ResultSuccess;
610 bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
611
612 is_valid = is_valid && (mii_v3.mii_name[0] != 0);
613
614 is_valid = is_valid && (mii_v3.mii_information.birth_month < 13);
615 is_valid = is_valid && (mii_v3.mii_information.birth_day < 32);
616 is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12);
617 is_valid = is_valid && (mii_v3.height < 128);
618 is_valid = is_valid && (mii_v3.build < 128);
619
620 is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12);
621 is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7);
622 is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12);
623 is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12);
624
625 is_valid = is_valid && (mii_v3.hair_style < 132);
626 is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8);
627
628 is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60);
629 is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6);
630 is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8);
631 is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7);
632 is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8);
633 is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13);
634 is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19);
635
636 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25);
637 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8);
638 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9);
639 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7);
640 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12);
641 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12);
642 is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19);
643
644 is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18);
645 is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9);
646 is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19);
647
648 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36);
649 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5);
650 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9);
651 is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7);
652 is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19);
653
654 is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6);
655 is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7);
656 is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17);
657
658 is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6);
659 is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8);
660
661 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9);
662 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6);
663 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8);
664 is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21);
665
666 is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2);
667 is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9);
668 is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17);
669 is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31);
670
671 return is_valid;
672} 151}
673 152
674std::vector<MiiInfoElement> MiiManager::GetDefault(SourceFlag source_flag) { 153Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
675 std::vector<MiiInfoElement> result; 154 s32& out_index) {
676 155
677 if ((source_flag & SourceFlag::Default) == SourceFlag::None) { 156 if (char_info.Verify() != ValidationResult::NoErrors) {
678 return result; 157 return ResultInvalidCharInfo;
679 } 158 }
680 159
681 for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) {
682 result.emplace_back(BuildDefault(index), Source::Default);
683 }
684
685 return result;
686}
687
688Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) {
689 constexpr u32 INVALID_INDEX{0xFFFFFFFF}; 160 constexpr u32 INVALID_INDEX{0xFFFFFFFF};
690 161
691 index = INVALID_INDEX; 162 out_index = INVALID_INDEX;
692 163
693 // TODO(bunnei): We don't implement the Mii database, so we can't have an index 164 // TODO(bunnei): We don't implement the Mii database, so we can't have an index
694 return ERROR_CANNOT_FIND_ENTRY; 165 return ResultNotFound;
166}
167
168void MiiManager::SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version) {
169 metadata.interface_version = version;
695} 170}
696 171
697} // namespace Service::Mii 172} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 45c2be3c8..a2e7a6d73 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -6,7 +6,10 @@
6#include <vector> 6#include <vector>
7 7
8#include "core/hle/result.h" 8#include "core/hle/result.h"
9#include "core/hle/service/mii/types.h" 9#include "core/hle/service/mii/mii_types.h"
10#include "core/hle/service/mii/types/char_info.h"
11#include "core/hle/service/mii/types/store_data.h"
12#include "core/hle/service/mii/types/ver3_store_data.h"
10 13
11namespace Service::Mii { 14namespace Service::Mii {
12 15
@@ -16,25 +19,30 @@ class MiiManager {
16public: 19public:
17 MiiManager(); 20 MiiManager();
18 21
19 bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter); 22 bool IsUpdated(DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
20 bool IsFullDatabase() const;
21 u32 GetCount(SourceFlag source_flag) const;
22 Result UpdateLatest(CharInfo* out_info, const CharInfo& info, SourceFlag source_flag);
23 CharInfo BuildRandom(Age age, Gender gender, Race race);
24 CharInfo BuildDefault(std::size_t index);
25 CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
26 bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
27 std::vector<MiiInfoElement> GetDefault(SourceFlag source_flag);
28 Result GetIndex(const CharInfo& info, u32& index);
29
30 // This is nn::mii::detail::Ver::StoreDataRaw::BuildFromStoreData
31 Ver3StoreData BuildFromStoreData(const CharInfo& mii) const;
32 23
33 // This is nn::mii::detail::NfpStoreDataExtentionRaw::SetFromStoreData 24 bool IsFullDatabase() const;
34 NfpStoreDataExtension SetFromStoreData(const CharInfo& mii) const; 25 u32 GetCount(const DatabaseSessionMetadata& metadata, SourceFlag source_flag) const;
26 Result UpdateLatest(DatabaseSessionMetadata& metadata, CharInfo& out_char_info,
27 const CharInfo& char_info, SourceFlag source_flag);
28 Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfoElement> out_elements,
29 u32& out_count, SourceFlag source_flag);
30 Result Get(const DatabaseSessionMetadata& metadata, std::span<CharInfo> out_char_info,
31 u32& out_count, SourceFlag source_flag);
32 void BuildDefault(CharInfo& out_char_info, u32 index) const;
33 void BuildBase(CharInfo& out_char_info, Gender gender) const;
34 void BuildRandom(CharInfo& out_char_info, Age age, Gender gender, Race race) const;
35 void ConvertV3ToCharInfo(CharInfo& out_char_info, const Ver3StoreData& mii_v3) const;
36 std::vector<CharInfoElement> GetDefault(SourceFlag source_flag);
37 Result GetIndex(const DatabaseSessionMetadata& metadata, const CharInfo& char_info,
38 s32& out_index);
39 void SetInterfaceVersion(DatabaseSessionMetadata& metadata, u32 version);
35 40
36private: 41private:
37 const Common::UUID user_id{}; 42 Result BuildDefault(std::span<CharInfoElement> out_elements, u32& out_count,
43 SourceFlag source_flag);
44 Result BuildDefault(std::span<CharInfo> out_char_info, u32& out_count, SourceFlag source_flag);
45
38 u64 update_counter{}; 46 u64 update_counter{};
39}; 47};
40 48
diff --git a/src/core/hle/service/mii/mii_result.h b/src/core/hle/service/mii/mii_result.h
new file mode 100644
index 000000000..021cb76da
--- /dev/null
+++ b/src/core/hle/service/mii/mii_result.h
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::Mii {
9
10constexpr Result ResultInvalidArgument{ErrorModule::Mii, 1};
11constexpr Result ResultInvalidArgumentSize{ErrorModule::Mii, 2};
12constexpr Result ResultNotUpdated{ErrorModule::Mii, 3};
13constexpr Result ResultNotFound{ErrorModule::Mii, 4};
14constexpr Result ResultDatabaseFull{ErrorModule::Mii, 5};
15constexpr Result ResultInvalidCharInfo{ErrorModule::Mii, 100};
16constexpr Result ResultInvalidStoreData{ErrorModule::Mii, 109};
17constexpr Result ResultInvalidOperation{ErrorModule::Mii, 202};
18constexpr Result ResultPermissionDenied{ErrorModule::Mii, 203};
19
20}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_types.h b/src/core/hle/service/mii/mii_types.h
new file mode 100644
index 000000000..95476f745
--- /dev/null
+++ b/src/core/hle/service/mii/mii_types.h
@@ -0,0 +1,694 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <type_traits>
8
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/uuid.h"
13
14namespace Service::Mii {
15
16constexpr u8 MaxHeight = 127;
17constexpr u8 MaxBuild = 127;
18constexpr u8 MaxType = 1;
19constexpr u8 MaxRegionMove = 3;
20constexpr u8 MaxEyeScale = 7;
21constexpr u8 MaxEyeAspect = 6;
22constexpr u8 MaxEyeRotate = 7;
23constexpr u8 MaxEyeX = 12;
24constexpr u8 MaxEyeY = 18;
25constexpr u8 MaxEyebrowScale = 8;
26constexpr u8 MaxEyebrowAspect = 6;
27constexpr u8 MaxEyebrowRotate = 11;
28constexpr u8 MaxEyebrowX = 12;
29constexpr u8 MaxEyebrowY = 18;
30constexpr u8 MaxNoseScale = 8;
31constexpr u8 MaxNoseY = 18;
32constexpr u8 MaxMouthScale = 8;
33constexpr u8 MaxMoutAspect = 6;
34constexpr u8 MaxMouthY = 18;
35constexpr u8 MaxMustacheScale = 8;
36constexpr u8 MasMustacheY = 16;
37constexpr u8 MaxGlassScale = 7;
38constexpr u8 MaxGlassY = 20;
39constexpr u8 MaxMoleScale = 8;
40constexpr u8 MaxMoleX = 16;
41constexpr u8 MaxMoleY = 30;
42constexpr u8 MaxVer3CommonColor = 7;
43constexpr u8 MaxVer3GlassType = 8;
44
45enum class Age : u8 {
46 Young,
47 Normal,
48 Old,
49 All, // Default
50
51 Max = All,
52};
53
54enum class Gender : u8 {
55 Male,
56 Female,
57 All, // Default
58
59 Max = Female,
60};
61
62enum class Race : u8 {
63 Black,
64 White,
65 Asian,
66 All, // Default
67
68 Max = All,
69};
70
71enum class HairType : u8 {
72 NormalLong, // Default
73 NormalShort,
74 NormalMedium,
75 NormalExtraLong,
76 NormalLongBottom,
77 NormalTwoPeaks,
78 PartingLong,
79 FrontLock,
80 PartingShort,
81 PartingExtraLongCurved,
82 PartingExtraLong,
83 PartingMiddleLong,
84 PartingSquared,
85 PartingLongBottom,
86 PeaksTop,
87 PeaksSquared,
88 PartingPeaks,
89 PeaksLongBottom,
90 Peaks,
91 PeaksRounded,
92 PeaksSide,
93 PeaksMedium,
94 PeaksLong,
95 PeaksRoundedLong,
96 PartingFrontPeaks,
97 PartingLongFront,
98 PartingLongRounded,
99 PartingFrontPeaksLong,
100 PartingExtraLongRounded,
101 LongRounded,
102 NormalUnknown1,
103 NormalUnknown2,
104 NormalUnknown3,
105 NormalUnknown4,
106 NormalUnknown5,
107 NormalUnknown6,
108 DreadLocks,
109 PlatedMats,
110 Caps,
111 Afro,
112 PlatedMatsLong,
113 Beanie,
114 Short,
115 ShortTopLongSide,
116 ShortUnknown1,
117 ShortUnknown2,
118 MilitaryParting,
119 Military,
120 ShortUnknown3,
121 ShortUnknown4,
122 ShortUnknown5,
123 ShortUnknown6,
124 NoneTop,
125 None,
126 LongUnknown1,
127 LongUnknown2,
128 LongUnknown3,
129 LongUnknown4,
130 LongUnknown5,
131 LongUnknown6,
132 LongUnknown7,
133 LongUnknown8,
134 LongUnknown9,
135 LongUnknown10,
136 LongUnknown11,
137 LongUnknown12,
138 LongUnknown13,
139 LongUnknown14,
140 LongUnknown15,
141 LongUnknown16,
142 LongUnknown17,
143 LongUnknown18,
144 LongUnknown19,
145 LongUnknown20,
146 LongUnknown21,
147 LongUnknown22,
148 LongUnknown23,
149 LongUnknown24,
150 LongUnknown25,
151 LongUnknown26,
152 LongUnknown27,
153 LongUnknown28,
154 LongUnknown29,
155 LongUnknown30,
156 LongUnknown31,
157 LongUnknown32,
158 LongUnknown33,
159 LongUnknown34,
160 LongUnknown35,
161 LongUnknown36,
162 LongUnknown37,
163 LongUnknown38,
164 LongUnknown39,
165 LongUnknown40,
166 LongUnknown41,
167 LongUnknown42,
168 LongUnknown43,
169 LongUnknown44,
170 LongUnknown45,
171 LongUnknown46,
172 LongUnknown47,
173 LongUnknown48,
174 LongUnknown49,
175 LongUnknown50,
176 LongUnknown51,
177 LongUnknown52,
178 LongUnknown53,
179 LongUnknown54,
180 LongUnknown55,
181 LongUnknown56,
182 LongUnknown57,
183 LongUnknown58,
184 LongUnknown59,
185 LongUnknown60,
186 LongUnknown61,
187 LongUnknown62,
188 LongUnknown63,
189 LongUnknown64,
190 LongUnknown65,
191 LongUnknown66,
192 TwoMediumFrontStrandsOneLongBackPonyTail,
193 TwoFrontStrandsLongBackPonyTail,
194 PartingFrontTwoLongBackPonyTails,
195 TwoFrontStrandsOneLongBackPonyTail,
196 LongBackPonyTail,
197 LongFrontTwoLongBackPonyTails,
198 StrandsTwoShortSidedPonyTails,
199 TwoMediumSidedPonyTails,
200 ShortFrontTwoBackPonyTails,
201 TwoShortSidedPonyTails,
202 TwoLongSidedPonyTails,
203 LongFrontTwoBackPonyTails,
204
205 Max = LongFrontTwoBackPonyTails,
206};
207
208enum class MoleType : u8 {
209 None, // Default
210 OneDot,
211
212 Max = OneDot,
213};
214
215enum class HairFlip : u8 {
216 Left, // Default
217 Right,
218
219 Max = Right,
220};
221
222enum class CommonColor : u8 {
223 // For simplicity common colors aren't listed
224 Max = 99,
225 Count = 100,
226};
227
228enum class FavoriteColor : u8 {
229 Red, // Default
230 Orange,
231 Yellow,
232 LimeGreen,
233 Green,
234 Blue,
235 LightBlue,
236 Pink,
237 Purple,
238 Brown,
239 White,
240 Black,
241
242 Max = Black,
243};
244
245enum class EyeType : u8 {
246 Normal, // Default
247 NormalLash,
248 WhiteLash,
249 WhiteNoBottom,
250 OvalAngledWhite,
251 AngryWhite,
252 DotLashType1,
253 Line,
254 DotLine,
255 OvalWhite,
256 RoundedWhite,
257 NormalShadow,
258 CircleWhite,
259 Circle,
260 CircleWhiteStroke,
261 NormalOvalNoBottom,
262 NormalOvalLarge,
263 NormalRoundedNoBottom,
264 SmallLash,
265 Small,
266 TwoSmall,
267 NormalLongLash,
268 WhiteTwoLashes,
269 WhiteThreeLashes,
270 DotAngry,
271 DotAngled,
272 Oval,
273 SmallWhite,
274 WhiteAngledNoBottom,
275 WhiteAngledNoLeft,
276 SmallWhiteTwoLashes,
277 LeafWhiteLash,
278 WhiteLargeNoBottom,
279 Dot,
280 DotLashType2,
281 DotThreeLashes,
282 WhiteOvalTop,
283 WhiteOvalBottom,
284 WhiteOvalBottomFlat,
285 WhiteOvalTwoLashes,
286 WhiteOvalThreeLashes,
287 WhiteOvalNoBottomTwoLashes,
288 DotWhite,
289 WhiteOvalTopFlat,
290 WhiteThinLeaf,
291 StarThreeLashes,
292 LineTwoLashes,
293 CrowsFeet,
294 WhiteNoBottomFlat,
295 WhiteNoBottomRounded,
296 WhiteSmallBottomLine,
297 WhiteNoBottomLash,
298 WhiteNoPartialBottomLash,
299 WhiteOvalBottomLine,
300 WhiteNoBottomLashTopLine,
301 WhiteNoPartialBottomTwoLashes,
302 NormalTopLine,
303 WhiteOvalLash,
304 RoundTired,
305 WhiteLarge,
306
307 Max = WhiteLarge,
308};
309
310enum class MouthType : u8 {
311 Neutral, // Default
312 NeutralLips,
313 Smile,
314 SmileStroke,
315 SmileTeeth,
316 LipsSmall,
317 LipsLarge,
318 Wave,
319 WaveAngrySmall,
320 NeutralStrokeLarge,
321 TeethSurprised,
322 LipsExtraLarge,
323 LipsUp,
324 NeutralDown,
325 Surprised,
326 TeethMiddle,
327 NeutralStroke,
328 LipsExtraSmall,
329 Malicious,
330 LipsDual,
331 NeutralComma,
332 NeutralUp,
333 TeethLarge,
334 WaveAngry,
335 LipsSexy,
336 SmileInverted,
337 LipsSexyOutline,
338 SmileRounded,
339 LipsTeeth,
340 NeutralOpen,
341 TeethRounded,
342 WaveAngrySmallInverted,
343 NeutralCommaInverted,
344 TeethFull,
345 SmileDownLine,
346 Kiss,
347
348 Max = Kiss,
349};
350
351enum class FontRegion : u8 {
352 Standard, // Default
353 China,
354 Korea,
355 Taiwan,
356
357 Max = Taiwan,
358};
359
360enum class FacelineType : u8 {
361 Sharp, // Default
362 Rounded,
363 SharpRounded,
364 SharpRoundedSmall,
365 Large,
366 LargeRounded,
367 SharpSmall,
368 Flat,
369 Bump,
370 Angular,
371 FlatRounded,
372 AngularSmall,
373
374 Max = AngularSmall,
375};
376
377enum class FacelineColor : u8 {
378 Beige, // Default
379 WarmBeige,
380 Natural,
381 Honey,
382 Chestnut,
383 Porcelain,
384 Ivory,
385 WarmIvory,
386 Almond,
387 Espresso,
388
389 Max = Espresso,
390 Count = Max + 1,
391};
392
393enum class FacelineWrinkle : u8 {
394 None, // Default
395 TearTroughs,
396 FacialPain,
397 Cheeks,
398 Folds,
399 UnderTheEyes,
400 SplitChin,
401 Chin,
402 BrowDroop,
403 MouthFrown,
404 CrowsFeet,
405 FoldsCrowsFrown,
406
407 Max = FoldsCrowsFrown,
408};
409
410enum class FacelineMake : u8 {
411 None, // Default
412 CheekPorcelain,
413 CheekNatural,
414 EyeShadowBlue,
415 CheekBlushPorcelain,
416 CheekBlushNatural,
417 CheekPorcelainEyeShadowBlue,
418 CheekPorcelainEyeShadowNatural,
419 CheekBlushPorcelainEyeShadowEspresso,
420 Freckles,
421 LionsManeBeard,
422 StubbleBeard,
423
424 Max = StubbleBeard,
425};
426
427enum class EyebrowType : u8 {
428 FlatAngledLarge, // Default
429 LowArchRoundedThin,
430 SoftAngledLarge,
431 MediumArchRoundedThin,
432 RoundedMedium,
433 LowArchMedium,
434 RoundedThin,
435 UpThin,
436 MediumArchRoundedMedium,
437 RoundedLarge,
438 UpLarge,
439 FlatAngledLargeInverted,
440 MediumArchFlat,
441 AngledThin,
442 HorizontalLarge,
443 HighArchFlat,
444 Flat,
445 MediumArchLarge,
446 LowArchThin,
447 RoundedThinInverted,
448 HighArchLarge,
449 Hairy,
450 Dotted,
451 None,
452
453 Max = None,
454};
455
456enum class NoseType : u8 {
457 Normal, // Default
458 Rounded,
459 Dot,
460 Arrow,
461 Roman,
462 Triangle,
463 Button,
464 RoundedInverted,
465 Potato,
466 Grecian,
467 Snub,
468 Aquiline,
469 ArrowLeft,
470 RoundedLarge,
471 Hooked,
472 Fat,
473 Droopy,
474 ArrowLarge,
475
476 Max = ArrowLarge,
477};
478
479enum class BeardType : u8 {
480 None,
481 Goatee,
482 GoateeLong,
483 LionsManeLong,
484 LionsMane,
485 Full,
486
487 Min = Goatee,
488 Max = Full,
489};
490
491enum class MustacheType : u8 {
492 None,
493 Walrus,
494 Pencil,
495 Horseshoe,
496 Normal,
497 Toothbrush,
498
499 Min = Walrus,
500 Max = Toothbrush,
501};
502
503enum class GlassType : u8 {
504 None,
505 Oval,
506 Wayfarer,
507 Rectangle,
508 TopRimless,
509 Rounded,
510 Oversized,
511 CatEye,
512 Square,
513 BottomRimless,
514 SemiOpaqueRounded,
515 SemiOpaqueCatEye,
516 SemiOpaqueOval,
517 SemiOpaqueRectangle,
518 SemiOpaqueAviator,
519 OpaqueRounded,
520 OpaqueCatEye,
521 OpaqueOval,
522 OpaqueRectangle,
523 OpaqueAviator,
524
525 Max = OpaqueAviator,
526 Count = Max + 1,
527};
528
529enum class BeardAndMustacheFlag : u32 {
530 Beard = 1,
531 Mustache,
532 All = Beard | Mustache,
533};
534DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
535
536enum class Source : u32 {
537 Database = 0,
538 Default = 1,
539 Account = 2,
540 Friend = 3,
541};
542
543enum class SourceFlag : u32 {
544 None = 0,
545 Database = 1 << 0,
546 Default = 1 << 1,
547};
548DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
549
550enum class ValidationResult : u32 {
551 NoErrors = 0x0,
552 InvalidBeardColor = 0x1,
553 InvalidBeardType = 0x2,
554 InvalidBuild = 0x3,
555 InvalidEyeAspect = 0x4,
556 InvalidEyeColor = 0x5,
557 InvalidEyeRotate = 0x6,
558 InvalidEyeScale = 0x7,
559 InvalidEyeType = 0x8,
560 InvalidEyeX = 0x9,
561 InvalidEyeY = 0xa,
562 InvalidEyebrowAspect = 0xb,
563 InvalidEyebrowColor = 0xc,
564 InvalidEyebrowRotate = 0xd,
565 InvalidEyebrowScale = 0xe,
566 InvalidEyebrowType = 0xf,
567 InvalidEyebrowX = 0x10,
568 InvalidEyebrowY = 0x11,
569 InvalidFacelineColor = 0x12,
570 InvalidFacelineMake = 0x13,
571 InvalidFacelineWrinkle = 0x14,
572 InvalidFacelineType = 0x15,
573 InvalidColor = 0x16,
574 InvalidFont = 0x17,
575 InvalidGender = 0x18,
576 InvalidGlassColor = 0x19,
577 InvalidGlassScale = 0x1a,
578 InvalidGlassType = 0x1b,
579 InvalidGlassY = 0x1c,
580 InvalidHairColor = 0x1d,
581 InvalidHairFlip = 0x1e,
582 InvalidHairType = 0x1f,
583 InvalidHeight = 0x20,
584 InvalidMoleScale = 0x21,
585 InvalidMoleType = 0x22,
586 InvalidMoleX = 0x23,
587 InvalidMoleY = 0x24,
588 InvalidMouthAspect = 0x25,
589 InvalidMouthColor = 0x26,
590 InvalidMouthScale = 0x27,
591 InvalidMouthType = 0x28,
592 InvalidMouthY = 0x29,
593 InvalidMustacheScale = 0x2a,
594 InvalidMustacheType = 0x2b,
595 InvalidMustacheY = 0x2c,
596 InvalidNoseScale = 0x2e,
597 InvalidNoseType = 0x2f,
598 InvalidNoseY = 0x30,
599 InvalidRegionMove = 0x31,
600 InvalidCreateId = 0x32,
601 InvalidName = 0x33,
602 InvalidType = 0x35,
603};
604
605struct Nickname {
606 static constexpr std::size_t MaxNameSize = 10;
607 std::array<char16_t, MaxNameSize> data;
608
609 // Checks for null, non-zero terminated or dirty strings
610 bool IsValid() const {
611 if (data[0] == 0) {
612 return false;
613 }
614
615 if (data[MaxNameSize] != 0) {
616 return false;
617 }
618 std::size_t index = 1;
619 while (data[index] != 0) {
620 index++;
621 }
622 while (index < MaxNameSize && data[index] == 0) {
623 index++;
624 }
625 return index == MaxNameSize;
626 }
627};
628static_assert(sizeof(Nickname) == 0x14, "Nickname is an invalid size");
629
630struct DefaultMii {
631 u32 face_type{};
632 u32 face_color{};
633 u32 face_wrinkle{};
634 u32 face_makeup{};
635 u32 hair_type{};
636 u32 hair_color{};
637 u32 hair_flip{};
638 u32 eye_type{};
639 u32 eye_color{};
640 u32 eye_scale{};
641 u32 eye_aspect{};
642 u32 eye_rotate{};
643 u32 eye_x{};
644 u32 eye_y{};
645 u32 eyebrow_type{};
646 u32 eyebrow_color{};
647 u32 eyebrow_scale{};
648 u32 eyebrow_aspect{};
649 u32 eyebrow_rotate{};
650 u32 eyebrow_x{};
651 u32 eyebrow_y{};
652 u32 nose_type{};
653 u32 nose_scale{};
654 u32 nose_y{};
655 u32 mouth_type{};
656 u32 mouth_color{};
657 u32 mouth_scale{};
658 u32 mouth_aspect{};
659 u32 mouth_y{};
660 u32 mustache_type{};
661 u32 beard_type{};
662 u32 beard_color{};
663 u32 mustache_scale{};
664 u32 mustache_y{};
665 u32 glasses_type{};
666 u32 glasses_color{};
667 u32 glasses_scale{};
668 u32 glasses_y{};
669 u32 mole_type{};
670 u32 mole_scale{};
671 u32 mole_x{};
672 u32 mole_y{};
673 u32 height{};
674 u32 weight{};
675 u32 gender{};
676 u32 favorite_color{};
677 u32 region_move{};
678 u32 font_region{};
679 u32 type{};
680 Nickname nickname;
681};
682static_assert(sizeof(DefaultMii) == 0xd8, "DefaultMii has incorrect size.");
683
684struct DatabaseSessionMetadata {
685 u32 interface_version;
686 u32 magic;
687 u64 update_counter;
688
689 bool IsInterfaceVersionSupported(u32 version) const {
690 return version <= interface_version;
691 }
692};
693
694} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii_util.h b/src/core/hle/service/mii/mii_util.h
new file mode 100644
index 000000000..ddb544c23
--- /dev/null
+++ b/src/core/hle/service/mii/mii_util.h
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <random>
7#include <span>
8
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "common/uuid.h"
12#include "core/hle/service/mii/mii_types.h"
13
14namespace Service::Mii {
15class MiiUtil {
16public:
17 static u16 CalculateCrc16(const void* data, std::size_t size) {
18 s32 crc{};
19 for (std::size_t i = 0; i < size; i++) {
20 crc ^= static_cast<const u8*>(data)[i] << 8;
21 for (std::size_t j = 0; j < 8; j++) {
22 crc <<= 1;
23 if ((crc & 0x10000) != 0) {
24 crc = (crc ^ 0x1021) & 0xFFFF;
25 }
26 }
27 }
28 return Common::swap16(static_cast<u16>(crc));
29 }
30
31 static Common::UUID MakeCreateId() {
32 return Common::UUID::MakeRandomRFC4122V4();
33 }
34
35 static Common::UUID GetDeviceId() {
36 // This should be nn::settings::detail::GetMiiAuthorId()
37 return Common::UUID::MakeDefault();
38 }
39
40 template <typename T>
41 static T GetRandomValue(T min, T max) {
42 std::random_device device;
43 std::mt19937 gen(device());
44 std::uniform_int_distribution<u64> distribution(static_cast<u64>(min),
45 static_cast<u64>(max));
46 return static_cast<T>(distribution(gen));
47 }
48
49 template <typename T>
50 static T GetRandomValue(T max) {
51 return GetRandomValue<T>({}, max);
52 }
53
54 static bool IsFontRegionValid(FontRegion font, std::span<const char16_t> text) {
55 // TODO: This function needs to check against the font tables
56 return true;
57 }
58};
59} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
deleted file mode 100644
index c2bec68d4..000000000
--- a/src/core/hle/service/mii/raw_data.h
+++ /dev/null
@@ -1,26 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "core/hle/service/mii/types.h"
9
10namespace Service::Mii::RawData {
11
12extern const std::array<Service::Mii::DefaultMii, 8> DefaultMii;
13extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline;
14extern const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor;
15extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle;
16extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup;
17extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType;
18extern const std::array<Service::Mii::RandomMiiData3, 9> RandomMiiHairColor;
19extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType;
20extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor;
21extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType;
22extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType;
23extern const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType;
24extern const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType;
25
26} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h
deleted file mode 100644
index c48d08d79..000000000
--- a/src/core/hle/service/mii/types.h
+++ /dev/null
@@ -1,553 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <type_traits>
8
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/uuid.h"
13
14namespace Service::Mii {
15
16enum class Age : u32 {
17 Young,
18 Normal,
19 Old,
20 All,
21};
22
23enum class BeardType : u32 {
24 None,
25 Beard1,
26 Beard2,
27 Beard3,
28 Beard4,
29 Beard5,
30};
31
32enum class BeardAndMustacheFlag : u32 {
33 Beard = 1,
34 Mustache,
35 All = Beard | Mustache,
36};
37DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
38
39enum class FontRegion : u32 {
40 Standard,
41 China,
42 Korea,
43 Taiwan,
44};
45
46enum class Gender : u32 {
47 Male,
48 Female,
49 All,
50 Maximum = Female,
51};
52
53enum class HairFlip : u32 {
54 Left,
55 Right,
56 Maximum = Right,
57};
58
59enum class MustacheType : u32 {
60 None,
61 Mustache1,
62 Mustache2,
63 Mustache3,
64 Mustache4,
65 Mustache5,
66};
67
68enum class Race : u32 {
69 Black,
70 White,
71 Asian,
72 All,
73};
74
75enum class Source : u32 {
76 Database = 0,
77 Default = 1,
78 Account = 2,
79 Friend = 3,
80};
81
82enum class SourceFlag : u32 {
83 None = 0,
84 Database = 1 << 0,
85 Default = 1 << 1,
86};
87DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
88
89// nn::mii::CharInfo
90struct CharInfo {
91 Common::UUID uuid;
92 std::array<char16_t, 11> name;
93 u8 font_region;
94 u8 favorite_color;
95 u8 gender;
96 u8 height;
97 u8 build;
98 u8 type;
99 u8 region_move;
100 u8 faceline_type;
101 u8 faceline_color;
102 u8 faceline_wrinkle;
103 u8 faceline_make;
104 u8 hair_type;
105 u8 hair_color;
106 u8 hair_flip;
107 u8 eye_type;
108 u8 eye_color;
109 u8 eye_scale;
110 u8 eye_aspect;
111 u8 eye_rotate;
112 u8 eye_x;
113 u8 eye_y;
114 u8 eyebrow_type;
115 u8 eyebrow_color;
116 u8 eyebrow_scale;
117 u8 eyebrow_aspect;
118 u8 eyebrow_rotate;
119 u8 eyebrow_x;
120 u8 eyebrow_y;
121 u8 nose_type;
122 u8 nose_scale;
123 u8 nose_y;
124 u8 mouth_type;
125 u8 mouth_color;
126 u8 mouth_scale;
127 u8 mouth_aspect;
128 u8 mouth_y;
129 u8 beard_color;
130 u8 beard_type;
131 u8 mustache_type;
132 u8 mustache_scale;
133 u8 mustache_y;
134 u8 glasses_type;
135 u8 glasses_color;
136 u8 glasses_scale;
137 u8 glasses_y;
138 u8 mole_type;
139 u8 mole_scale;
140 u8 mole_x;
141 u8 mole_y;
142 u8 padding;
143};
144static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
145static_assert(std::has_unique_object_representations_v<CharInfo>,
146 "All bits of CharInfo must contribute to its value.");
147
148#pragma pack(push, 4)
149
150struct MiiInfoElement {
151 MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {}
152
153 CharInfo info{};
154 Source source{};
155};
156static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
157
158struct MiiStoreBitFields {
159 union {
160 u32 word_0{};
161
162 BitField<0, 8, u32> hair_type;
163 BitField<8, 7, u32> height;
164 BitField<15, 1, u32> mole_type;
165 BitField<16, 7, u32> build;
166 BitField<23, 1, HairFlip> hair_flip;
167 BitField<24, 7, u32> hair_color;
168 BitField<31, 1, u32> type;
169 };
170
171 union {
172 u32 word_1{};
173
174 BitField<0, 7, u32> eye_color;
175 BitField<7, 1, Gender> gender;
176 BitField<8, 7, u32> eyebrow_color;
177 BitField<16, 7, u32> mouth_color;
178 BitField<24, 7, u32> beard_color;
179 };
180
181 union {
182 u32 word_2{};
183
184 BitField<0, 7, u32> glasses_color;
185 BitField<8, 6, u32> eye_type;
186 BitField<14, 2, u32> region_move;
187 BitField<16, 6, u32> mouth_type;
188 BitField<22, 2, FontRegion> font_region;
189 BitField<24, 5, u32> eye_y;
190 BitField<29, 3, u32> glasses_scale;
191 };
192
193 union {
194 u32 word_3{};
195
196 BitField<0, 5, u32> eyebrow_type;
197 BitField<5, 3, MustacheType> mustache_type;
198 BitField<8, 5, u32> nose_type;
199 BitField<13, 3, BeardType> beard_type;
200 BitField<16, 5, u32> nose_y;
201 BitField<21, 3, u32> mouth_aspect;
202 BitField<24, 5, u32> mouth_y;
203 BitField<29, 3, u32> eyebrow_aspect;
204 };
205
206 union {
207 u32 word_4{};
208
209 BitField<0, 5, u32> mustache_y;
210 BitField<5, 3, u32> eye_rotate;
211 BitField<8, 5, u32> glasses_y;
212 BitField<13, 3, u32> eye_aspect;
213 BitField<16, 5, u32> mole_x;
214 BitField<21, 3, u32> eye_scale;
215 BitField<24, 5, u32> mole_y;
216 };
217
218 union {
219 u32 word_5{};
220
221 BitField<0, 5, u32> glasses_type;
222 BitField<8, 4, u32> favorite_color;
223 BitField<12, 4, u32> faceline_type;
224 BitField<16, 4, u32> faceline_color;
225 BitField<20, 4, u32> faceline_wrinkle;
226 BitField<24, 4, u32> faceline_makeup;
227 BitField<28, 4, u32> eye_x;
228 };
229
230 union {
231 u32 word_6{};
232
233 BitField<0, 4, u32> eyebrow_scale;
234 BitField<4, 4, u32> eyebrow_rotate;
235 BitField<8, 4, u32> eyebrow_x;
236 BitField<12, 4, u32> eyebrow_y;
237 BitField<16, 4, u32> nose_scale;
238 BitField<20, 4, u32> mouth_scale;
239 BitField<24, 4, u32> mustache_scale;
240 BitField<28, 4, u32> mole_scale;
241 };
242};
243static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
244static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
245 "MiiStoreBitFields is not trivially copyable.");
246
247// This is nn::mii::Ver3StoreData
248// Based on citra HLE::Applets::MiiData and PretendoNetwork.
249// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
250// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
251struct Ver3StoreData {
252 u8 version;
253 union {
254 u8 raw;
255
256 BitField<0, 1, u8> allow_copying;
257 BitField<1, 1, u8> profanity_flag;
258 BitField<2, 2, u8> region_lock;
259 BitField<4, 2, u8> character_set;
260 } region_information;
261 u16_be mii_id;
262 u64_be system_id;
263 u32_be specialness_and_creation_date;
264 std::array<u8, 0x6> creator_mac;
265 u16_be padding;
266 union {
267 u16 raw;
268
269 BitField<0, 1, u16> gender;
270 BitField<1, 4, u16> birth_month;
271 BitField<5, 5, u16> birth_day;
272 BitField<10, 4, u16> favorite_color;
273 BitField<14, 1, u16> favorite;
274 } mii_information;
275 std::array<char16_t, 0xA> mii_name;
276 u8 height;
277 u8 build;
278 union {
279 u8 raw;
280
281 BitField<0, 1, u8> disable_sharing;
282 BitField<1, 4, u8> face_shape;
283 BitField<5, 3, u8> skin_color;
284 } appearance_bits1;
285 union {
286 u8 raw;
287
288 BitField<0, 4, u8> wrinkles;
289 BitField<4, 4, u8> makeup;
290 } appearance_bits2;
291 u8 hair_style;
292 union {
293 u8 raw;
294
295 BitField<0, 3, u8> hair_color;
296 BitField<3, 1, u8> flip_hair;
297 } appearance_bits3;
298 union {
299 u32 raw;
300
301 BitField<0, 6, u32> eye_type;
302 BitField<6, 3, u32> eye_color;
303 BitField<9, 4, u32> eye_scale;
304 BitField<13, 3, u32> eye_vertical_stretch;
305 BitField<16, 5, u32> eye_rotation;
306 BitField<21, 4, u32> eye_spacing;
307 BitField<25, 5, u32> eye_y_position;
308 } appearance_bits4;
309 union {
310 u32 raw;
311
312 BitField<0, 5, u32> eyebrow_style;
313 BitField<5, 3, u32> eyebrow_color;
314 BitField<8, 4, u32> eyebrow_scale;
315 BitField<12, 3, u32> eyebrow_yscale;
316 BitField<16, 4, u32> eyebrow_rotation;
317 BitField<21, 4, u32> eyebrow_spacing;
318 BitField<25, 5, u32> eyebrow_y_position;
319 } appearance_bits5;
320 union {
321 u16 raw;
322
323 BitField<0, 5, u16> nose_type;
324 BitField<5, 4, u16> nose_scale;
325 BitField<9, 5, u16> nose_y_position;
326 } appearance_bits6;
327 union {
328 u16 raw;
329
330 BitField<0, 6, u16> mouth_type;
331 BitField<6, 3, u16> mouth_color;
332 BitField<9, 4, u16> mouth_scale;
333 BitField<13, 3, u16> mouth_horizontal_stretch;
334 } appearance_bits7;
335 union {
336 u8 raw;
337
338 BitField<0, 5, u8> mouth_y_position;
339 BitField<5, 3, u8> mustache_type;
340 } appearance_bits8;
341 u8 allow_copying;
342 union {
343 u16 raw;
344
345 BitField<0, 3, u16> bear_type;
346 BitField<3, 3, u16> facial_hair_color;
347 BitField<6, 4, u16> mustache_scale;
348 BitField<10, 5, u16> mustache_y_position;
349 } appearance_bits9;
350 union {
351 u16 raw;
352
353 BitField<0, 4, u16> glasses_type;
354 BitField<4, 3, u16> glasses_color;
355 BitField<7, 4, u16> glasses_scale;
356 BitField<11, 5, u16> glasses_y_position;
357 } appearance_bits10;
358 union {
359 u16 raw;
360
361 BitField<0, 1, u16> mole_enabled;
362 BitField<1, 4, u16> mole_scale;
363 BitField<5, 5, u16> mole_x_position;
364 BitField<10, 5, u16> mole_y_position;
365 } appearance_bits11;
366
367 std::array<u16_le, 0xA> author_name;
368 INSERT_PADDING_BYTES(0x2);
369 u16_be crc;
370};
371static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
372
373struct NfpStoreDataExtension {
374 u8 faceline_color;
375 u8 hair_color;
376 u8 eye_color;
377 u8 eyebrow_color;
378 u8 mouth_color;
379 u8 beard_color;
380 u8 glass_color;
381 u8 glass_type;
382};
383static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size");
384
385constexpr std::array<u8, 0x10> Ver3FacelineColorTable{
386 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5,
387};
388
389constexpr std::array<u8, 100> Ver3HairColorTable{
390 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0,
391 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
392 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4,
393 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5,
394 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7,
395 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4,
396};
397
398constexpr std::array<u8, 100> Ver3EyeColorTable{
399 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4,
400 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
401 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4,
402 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
403 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2,
404 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
405};
406
407constexpr std::array<u8, 100> Ver3MouthlineColorTable{
408 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
409 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
410 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
411 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
412 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3,
413 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3,
414};
415
416constexpr std::array<u8, 100> Ver3GlassColorTable{
417 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3,
418 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
419 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
420 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5,
421 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4,
422 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
423};
424
425constexpr std::array<u8, 20> Ver3GlassTypeTable{
426 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1,
427 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7,
428};
429
430struct MiiStoreData {
431 using Name = std::array<char16_t, 10>;
432
433 MiiStoreData();
434 MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
435 const Common::UUID& user_id);
436
437 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
438 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
439 // not suitable for our uses.
440 struct {
441 std::array<u8, 0x1C> data{};
442 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
443
444 Name name{};
445 Common::UUID uuid{};
446 } data;
447
448 u16 data_crc{};
449 u16 device_crc{};
450};
451static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
452
453struct MiiStoreDataElement {
454 MiiStoreData data{};
455 Source source{};
456};
457static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
458
459struct MiiDatabase {
460 u32 magic{}; // 'NFDB'
461 std::array<MiiStoreData, 0x64> miis{};
462 INSERT_PADDING_BYTES(1);
463 u8 count{};
464 u16 crc{};
465};
466static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
467
468struct RandomMiiValues {
469 std::array<u8, 0xbc> values{};
470};
471static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
472
473struct RandomMiiData4 {
474 Gender gender{};
475 Age age{};
476 Race race{};
477 u32 values_count{};
478 std::array<u32, 47> values{};
479};
480static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
481
482struct RandomMiiData3 {
483 u32 arg_1;
484 u32 arg_2;
485 u32 values_count;
486 std::array<u32, 47> values{};
487};
488static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
489
490struct RandomMiiData2 {
491 u32 arg_1;
492 u32 values_count;
493 std::array<u32, 47> values{};
494};
495static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
496
497struct DefaultMii {
498 u32 face_type{};
499 u32 face_color{};
500 u32 face_wrinkle{};
501 u32 face_makeup{};
502 u32 hair_type{};
503 u32 hair_color{};
504 u32 hair_flip{};
505 u32 eye_type{};
506 u32 eye_color{};
507 u32 eye_scale{};
508 u32 eye_aspect{};
509 u32 eye_rotate{};
510 u32 eye_x{};
511 u32 eye_y{};
512 u32 eyebrow_type{};
513 u32 eyebrow_color{};
514 u32 eyebrow_scale{};
515 u32 eyebrow_aspect{};
516 u32 eyebrow_rotate{};
517 u32 eyebrow_x{};
518 u32 eyebrow_y{};
519 u32 nose_type{};
520 u32 nose_scale{};
521 u32 nose_y{};
522 u32 mouth_type{};
523 u32 mouth_color{};
524 u32 mouth_scale{};
525 u32 mouth_aspect{};
526 u32 mouth_y{};
527 u32 mustache_type{};
528 u32 beard_type{};
529 u32 beard_color{};
530 u32 mustache_scale{};
531 u32 mustache_y{};
532 u32 glasses_type{};
533 u32 glasses_color{};
534 u32 glasses_scale{};
535 u32 glasses_y{};
536 u32 mole_type{};
537 u32 mole_scale{};
538 u32 mole_x{};
539 u32 mole_y{};
540 u32 height{};
541 u32 weight{};
542 Gender gender{};
543 u32 favorite_color{};
544 u32 region{};
545 FontRegion font_region{};
546 u32 type{};
547 INSERT_PADDING_WORDS(5);
548};
549static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
550
551#pragma pack(pop)
552
553} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/char_info.cpp b/src/core/hle/service/mii/types/char_info.cpp
new file mode 100644
index 000000000..bb948c628
--- /dev/null
+++ b/src/core/hle/service/mii/types/char_info.cpp
@@ -0,0 +1,482 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/mii/types/char_info.h"
5#include "core/hle/service/mii/types/store_data.h"
6
7namespace Service::Mii {
8
9void CharInfo::SetFromStoreData(const StoreData& store_data) {
10 name = store_data.GetNickname();
11 null_terminator = '\0';
12 create_id = store_data.GetCreateId();
13 font_region = store_data.GetFontRegion();
14 favorite_color = store_data.GetFavoriteColor();
15 gender = store_data.GetGender();
16 height = store_data.GetHeight();
17 build = store_data.GetBuild();
18 type = store_data.GetType();
19 region_move = store_data.GetRegionMove();
20 faceline_type = store_data.GetFacelineType();
21 faceline_color = store_data.GetFacelineColor();
22 faceline_wrinkle = store_data.GetFacelineWrinkle();
23 faceline_make = store_data.GetFacelineMake();
24 hair_type = store_data.GetHairType();
25 hair_color = store_data.GetHairColor();
26 hair_flip = store_data.GetHairFlip();
27 eye_type = store_data.GetEyeType();
28 eye_color = store_data.GetEyeColor();
29 eye_scale = store_data.GetEyeScale();
30 eye_aspect = store_data.GetEyeAspect();
31 eye_rotate = store_data.GetEyeRotate();
32 eye_x = store_data.GetEyeX();
33 eye_y = store_data.GetEyeY();
34 eyebrow_type = store_data.GetEyebrowType();
35 eyebrow_color = store_data.GetEyebrowColor();
36 eyebrow_scale = store_data.GetEyebrowScale();
37 eyebrow_aspect = store_data.GetEyebrowAspect();
38 eyebrow_rotate = store_data.GetEyebrowRotate();
39 eyebrow_x = store_data.GetEyebrowX();
40 eyebrow_y = store_data.GetEyebrowY();
41 nose_type = store_data.GetNoseType();
42 nose_scale = store_data.GetNoseScale();
43 nose_y = store_data.GetNoseY();
44 mouth_type = store_data.GetMouthType();
45 mouth_color = store_data.GetMouthColor();
46 mouth_scale = store_data.GetMouthScale();
47 mouth_aspect = store_data.GetMouthAspect();
48 mouth_y = store_data.GetMouthY();
49 beard_color = store_data.GetBeardColor();
50 beard_type = store_data.GetBeardType();
51 mustache_type = store_data.GetMustacheType();
52 mustache_scale = store_data.GetMustacheScale();
53 mustache_y = store_data.GetMustacheY();
54 glass_type = store_data.GetGlassType();
55 glass_color = store_data.GetGlassColor();
56 glass_scale = store_data.GetGlassScale();
57 glass_y = store_data.GetGlassY();
58 mole_type = store_data.GetMoleType();
59 mole_scale = store_data.GetMoleScale();
60 mole_x = store_data.GetMoleX();
61 mole_y = store_data.GetMoleY();
62 padding = '\0';
63}
64
65ValidationResult CharInfo::Verify() const {
66 if (!create_id.IsValid()) {
67 return ValidationResult::InvalidCreateId;
68 }
69 if (!name.IsValid()) {
70 return ValidationResult::InvalidName;
71 }
72 if (font_region > FontRegion::Max) {
73 return ValidationResult::InvalidFont;
74 }
75 if (favorite_color > FavoriteColor::Max) {
76 return ValidationResult::InvalidColor;
77 }
78 if (gender > Gender::Max) {
79 return ValidationResult::InvalidGender;
80 }
81 if (height > MaxHeight) {
82 return ValidationResult::InvalidHeight;
83 }
84 if (build > MaxBuild) {
85 return ValidationResult::InvalidBuild;
86 }
87 if (type > MaxType) {
88 return ValidationResult::InvalidType;
89 }
90 if (region_move > MaxRegionMove) {
91 return ValidationResult::InvalidRegionMove;
92 }
93 if (faceline_type > FacelineType::Max) {
94 return ValidationResult::InvalidFacelineType;
95 }
96 if (faceline_color > FacelineColor::Max) {
97 return ValidationResult::InvalidFacelineColor;
98 }
99 if (faceline_wrinkle > FacelineWrinkle::Max) {
100 return ValidationResult::InvalidFacelineWrinkle;
101 }
102 if (faceline_make > FacelineMake::Max) {
103 return ValidationResult::InvalidFacelineMake;
104 }
105 if (hair_type > HairType::Max) {
106 return ValidationResult::InvalidHairType;
107 }
108 if (hair_color > CommonColor::Max) {
109 return ValidationResult::InvalidHairColor;
110 }
111 if (hair_flip > HairFlip::Max) {
112 return ValidationResult::InvalidHairFlip;
113 }
114 if (eye_type > EyeType::Max) {
115 return ValidationResult::InvalidEyeType;
116 }
117 if (eye_color > CommonColor::Max) {
118 return ValidationResult::InvalidEyeColor;
119 }
120 if (eye_scale > MaxEyeScale) {
121 return ValidationResult::InvalidEyeScale;
122 }
123 if (eye_aspect > MaxEyeAspect) {
124 return ValidationResult::InvalidEyeAspect;
125 }
126 if (eye_rotate > MaxEyeX) {
127 return ValidationResult::InvalidEyeRotate;
128 }
129 if (eye_x > MaxEyeX) {
130 return ValidationResult::InvalidEyeX;
131 }
132 if (eye_y > MaxEyeY) {
133 return ValidationResult::InvalidEyeY;
134 }
135 if (eyebrow_type > EyebrowType::Max) {
136 return ValidationResult::InvalidEyebrowType;
137 }
138 if (eyebrow_color > CommonColor::Max) {
139 return ValidationResult::InvalidEyebrowColor;
140 }
141 if (eyebrow_scale > MaxEyebrowScale) {
142 return ValidationResult::InvalidEyebrowScale;
143 }
144 if (eyebrow_aspect > MaxEyebrowAspect) {
145 return ValidationResult::InvalidEyebrowAspect;
146 }
147 if (eyebrow_rotate > MaxEyebrowRotate) {
148 return ValidationResult::InvalidEyebrowRotate;
149 }
150 if (eyebrow_x > MaxEyebrowX) {
151 return ValidationResult::InvalidEyebrowX;
152 }
153 if (eyebrow_y > MaxEyebrowY) {
154 return ValidationResult::InvalidEyebrowY;
155 }
156 if (nose_type > NoseType::Max) {
157 return ValidationResult::InvalidNoseType;
158 }
159 if (nose_scale > MaxNoseScale) {
160 return ValidationResult::InvalidNoseScale;
161 }
162 if (nose_y > MaxNoseY) {
163 return ValidationResult::InvalidNoseY;
164 }
165 if (mouth_type > MouthType::Max) {
166 return ValidationResult::InvalidMouthType;
167 }
168 if (mouth_color > CommonColor::Max) {
169 return ValidationResult::InvalidMouthColor;
170 }
171 if (mouth_scale > MaxMouthScale) {
172 return ValidationResult::InvalidMouthScale;
173 }
174 if (mouth_aspect > MaxMoutAspect) {
175 return ValidationResult::InvalidMouthAspect;
176 }
177 if (mouth_y > MaxMouthY) {
178 return ValidationResult::InvalidMoleY;
179 }
180 if (beard_color > CommonColor::Max) {
181 return ValidationResult::InvalidBeardColor;
182 }
183 if (beard_type > BeardType::Max) {
184 return ValidationResult::InvalidBeardType;
185 }
186 if (mustache_type > MustacheType::Max) {
187 return ValidationResult::InvalidMustacheType;
188 }
189 if (mustache_scale > MaxMustacheScale) {
190 return ValidationResult::InvalidMustacheScale;
191 }
192 if (mustache_y > MasMustacheY) {
193 return ValidationResult::InvalidMustacheY;
194 }
195 if (glass_type > GlassType::Max) {
196 return ValidationResult::InvalidGlassType;
197 }
198 if (glass_color > CommonColor::Max) {
199 return ValidationResult::InvalidGlassColor;
200 }
201 if (glass_scale > MaxGlassScale) {
202 return ValidationResult::InvalidGlassScale;
203 }
204 if (glass_y > MaxGlassY) {
205 return ValidationResult::InvalidGlassY;
206 }
207 if (mole_type > MoleType::Max) {
208 return ValidationResult::InvalidMoleType;
209 }
210 if (mole_scale > MaxMoleScale) {
211 return ValidationResult::InvalidMoleScale;
212 }
213 if (mole_x > MaxMoleX) {
214 return ValidationResult::InvalidMoleX;
215 }
216 if (mole_y > MaxMoleY) {
217 return ValidationResult::InvalidMoleY;
218 }
219 return ValidationResult::NoErrors;
220}
221
222Common::UUID CharInfo::GetCreateId() const {
223 return create_id;
224}
225
226Nickname CharInfo::GetNickname() const {
227 return name;
228}
229
230FontRegion CharInfo::GetFontRegion() const {
231 return font_region;
232}
233
234FavoriteColor CharInfo::GetFavoriteColor() const {
235 return favorite_color;
236}
237
238Gender CharInfo::GetGender() const {
239 return gender;
240}
241
242u8 CharInfo::GetHeight() const {
243 return height;
244}
245
246u8 CharInfo::GetBuild() const {
247 return build;
248}
249
250u8 CharInfo::GetType() const {
251 return type;
252}
253
254u8 CharInfo::GetRegionMove() const {
255 return region_move;
256}
257
258FacelineType CharInfo::GetFacelineType() const {
259 return faceline_type;
260}
261
262FacelineColor CharInfo::GetFacelineColor() const {
263 return faceline_color;
264}
265
266FacelineWrinkle CharInfo::GetFacelineWrinkle() const {
267 return faceline_wrinkle;
268}
269
270FacelineMake CharInfo::GetFacelineMake() const {
271 return faceline_make;
272}
273
274HairType CharInfo::GetHairType() const {
275 return hair_type;
276}
277
278CommonColor CharInfo::GetHairColor() const {
279 return hair_color;
280}
281
282HairFlip CharInfo::GetHairFlip() const {
283 return hair_flip;
284}
285
286EyeType CharInfo::GetEyeType() const {
287 return eye_type;
288}
289
290CommonColor CharInfo::GetEyeColor() const {
291 return eye_color;
292}
293
294u8 CharInfo::GetEyeScale() const {
295 return eye_scale;
296}
297
298u8 CharInfo::GetEyeAspect() const {
299 return eye_aspect;
300}
301
302u8 CharInfo::GetEyeRotate() const {
303 return eye_rotate;
304}
305
306u8 CharInfo::GetEyeX() const {
307 return eye_x;
308}
309
310u8 CharInfo::GetEyeY() const {
311 return eye_y;
312}
313
314EyebrowType CharInfo::GetEyebrowType() const {
315 return eyebrow_type;
316}
317
318CommonColor CharInfo::GetEyebrowColor() const {
319 return eyebrow_color;
320}
321
322u8 CharInfo::GetEyebrowScale() const {
323 return eyebrow_scale;
324}
325
326u8 CharInfo::GetEyebrowAspect() const {
327 return eyebrow_aspect;
328}
329
330u8 CharInfo::GetEyebrowRotate() const {
331 return eyebrow_rotate;
332}
333
334u8 CharInfo::GetEyebrowX() const {
335 return eyebrow_x;
336}
337
338u8 CharInfo::GetEyebrowY() const {
339 return eyebrow_y;
340}
341
342NoseType CharInfo::GetNoseType() const {
343 return nose_type;
344}
345
346u8 CharInfo::GetNoseScale() const {
347 return nose_scale;
348}
349
350u8 CharInfo::GetNoseY() const {
351 return nose_y;
352}
353
354MouthType CharInfo::GetMouthType() const {
355 return mouth_type;
356}
357
358CommonColor CharInfo::GetMouthColor() const {
359 return mouth_color;
360}
361
362u8 CharInfo::GetMouthScale() const {
363 return mouth_scale;
364}
365
366u8 CharInfo::GetMouthAspect() const {
367 return mouth_aspect;
368}
369
370u8 CharInfo::GetMouthY() const {
371 return mouth_y;
372}
373
374CommonColor CharInfo::GetBeardColor() const {
375 return beard_color;
376}
377
378BeardType CharInfo::GetBeardType() const {
379 return beard_type;
380}
381
382MustacheType CharInfo::GetMustacheType() const {
383 return mustache_type;
384}
385
386u8 CharInfo::GetMustacheScale() const {
387 return mustache_scale;
388}
389
390u8 CharInfo::GetMustacheY() const {
391 return mustache_y;
392}
393
394GlassType CharInfo::GetGlassType() const {
395 return glass_type;
396}
397
398CommonColor CharInfo::GetGlassColor() const {
399 return glass_color;
400}
401
402u8 CharInfo::GetGlassScale() const {
403 return glass_scale;
404}
405
406u8 CharInfo::GetGlassY() const {
407 return glass_y;
408}
409
410MoleType CharInfo::GetMoleType() const {
411 return mole_type;
412}
413
414u8 CharInfo::GetMoleScale() const {
415 return mole_scale;
416}
417
418u8 CharInfo::GetMoleX() const {
419 return mole_x;
420}
421
422u8 CharInfo::GetMoleY() const {
423 return mole_y;
424}
425
426bool CharInfo::operator==(const CharInfo& info) {
427 bool is_identical = info.Verify() == ValidationResult::NoErrors;
428 is_identical &= name.data == info.GetNickname().data;
429 is_identical &= create_id == info.GetCreateId();
430 is_identical &= font_region == info.GetFontRegion();
431 is_identical &= favorite_color == info.GetFavoriteColor();
432 is_identical &= gender == info.GetGender();
433 is_identical &= height == info.GetHeight();
434 is_identical &= build == info.GetBuild();
435 is_identical &= type == info.GetType();
436 is_identical &= region_move == info.GetRegionMove();
437 is_identical &= faceline_type == info.GetFacelineType();
438 is_identical &= faceline_color == info.GetFacelineColor();
439 is_identical &= faceline_wrinkle == info.GetFacelineWrinkle();
440 is_identical &= faceline_make == info.GetFacelineMake();
441 is_identical &= hair_type == info.GetHairType();
442 is_identical &= hair_color == info.GetHairColor();
443 is_identical &= hair_flip == info.GetHairFlip();
444 is_identical &= eye_type == info.GetEyeType();
445 is_identical &= eye_color == info.GetEyeColor();
446 is_identical &= eye_scale == info.GetEyeScale();
447 is_identical &= eye_aspect == info.GetEyeAspect();
448 is_identical &= eye_rotate == info.GetEyeRotate();
449 is_identical &= eye_x == info.GetEyeX();
450 is_identical &= eye_y == info.GetEyeY();
451 is_identical &= eyebrow_type == info.GetEyebrowType();
452 is_identical &= eyebrow_color == info.GetEyebrowColor();
453 is_identical &= eyebrow_scale == info.GetEyebrowScale();
454 is_identical &= eyebrow_aspect == info.GetEyebrowAspect();
455 is_identical &= eyebrow_rotate == info.GetEyebrowRotate();
456 is_identical &= eyebrow_x == info.GetEyebrowX();
457 is_identical &= eyebrow_y == info.GetEyebrowY();
458 is_identical &= nose_type == info.GetNoseType();
459 is_identical &= nose_scale == info.GetNoseScale();
460 is_identical &= nose_y == info.GetNoseY();
461 is_identical &= mouth_type == info.GetMouthType();
462 is_identical &= mouth_color == info.GetMouthColor();
463 is_identical &= mouth_scale == info.GetMouthScale();
464 is_identical &= mouth_aspect == info.GetMouthAspect();
465 is_identical &= mouth_y == info.GetMouthY();
466 is_identical &= beard_color == info.GetBeardColor();
467 is_identical &= beard_type == info.GetBeardType();
468 is_identical &= mustache_type == info.GetMustacheType();
469 is_identical &= mustache_scale == info.GetMustacheScale();
470 is_identical &= mustache_y == info.GetMustacheY();
471 is_identical &= glass_type == info.GetGlassType();
472 is_identical &= glass_color == info.GetGlassColor();
473 is_identical &= glass_scale == info.GetGlassScale();
474 is_identical &= glass_y == info.GetGlassY();
475 is_identical &= mole_type == info.GetMoleType();
476 is_identical &= mole_scale == info.GetMoleScale();
477 is_identical &= mole_x == info.GetMoleX();
478 is_identical &= mole_y == info.GetMoleY();
479 return is_identical;
480}
481
482} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/char_info.h b/src/core/hle/service/mii/types/char_info.h
new file mode 100644
index 000000000..d069b221f
--- /dev/null
+++ b/src/core/hle/service/mii/types/char_info.h
@@ -0,0 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/mii/mii_types.h"
7
8namespace Service::Mii {
9class StoreData;
10
11// This is nn::mii::detail::CharInfoRaw
12class CharInfo {
13public:
14 void SetFromStoreData(const StoreData& store_data_raw);
15
16 ValidationResult Verify() const;
17
18 Common::UUID GetCreateId() const;
19 Nickname GetNickname() const;
20 FontRegion GetFontRegion() const;
21 FavoriteColor GetFavoriteColor() const;
22 Gender GetGender() const;
23 u8 GetHeight() const;
24 u8 GetBuild() const;
25 u8 GetType() const;
26 u8 GetRegionMove() const;
27 FacelineType GetFacelineType() const;
28 FacelineColor GetFacelineColor() const;
29 FacelineWrinkle GetFacelineWrinkle() const;
30 FacelineMake GetFacelineMake() const;
31 HairType GetHairType() const;
32 CommonColor GetHairColor() const;
33 HairFlip GetHairFlip() const;
34 EyeType GetEyeType() const;
35 CommonColor GetEyeColor() const;
36 u8 GetEyeScale() const;
37 u8 GetEyeAspect() const;
38 u8 GetEyeRotate() const;
39 u8 GetEyeX() const;
40 u8 GetEyeY() const;
41 EyebrowType GetEyebrowType() const;
42 CommonColor GetEyebrowColor() const;
43 u8 GetEyebrowScale() const;
44 u8 GetEyebrowAspect() const;
45 u8 GetEyebrowRotate() const;
46 u8 GetEyebrowX() const;
47 u8 GetEyebrowY() const;
48 NoseType GetNoseType() const;
49 u8 GetNoseScale() const;
50 u8 GetNoseY() const;
51 MouthType GetMouthType() const;
52 CommonColor GetMouthColor() const;
53 u8 GetMouthScale() const;
54 u8 GetMouthAspect() const;
55 u8 GetMouthY() const;
56 CommonColor GetBeardColor() const;
57 BeardType GetBeardType() const;
58 MustacheType GetMustacheType() const;
59 u8 GetMustacheScale() const;
60 u8 GetMustacheY() const;
61 GlassType GetGlassType() const;
62 CommonColor GetGlassColor() const;
63 u8 GetGlassScale() const;
64 u8 GetGlassY() const;
65 MoleType GetMoleType() const;
66 u8 GetMoleScale() const;
67 u8 GetMoleX() const;
68 u8 GetMoleY() const;
69
70 bool operator==(const CharInfo& info);
71
72private:
73 Common::UUID create_id;
74 Nickname name;
75 u16 null_terminator;
76 FontRegion font_region;
77 FavoriteColor favorite_color;
78 Gender gender;
79 u8 height;
80 u8 build;
81 u8 type;
82 u8 region_move;
83 FacelineType faceline_type;
84 FacelineColor faceline_color;
85 FacelineWrinkle faceline_wrinkle;
86 FacelineMake faceline_make;
87 HairType hair_type;
88 CommonColor hair_color;
89 HairFlip hair_flip;
90 EyeType eye_type;
91 CommonColor eye_color;
92 u8 eye_scale;
93 u8 eye_aspect;
94 u8 eye_rotate;
95 u8 eye_x;
96 u8 eye_y;
97 EyebrowType eyebrow_type;
98 CommonColor eyebrow_color;
99 u8 eyebrow_scale;
100 u8 eyebrow_aspect;
101 u8 eyebrow_rotate;
102 u8 eyebrow_x;
103 u8 eyebrow_y;
104 NoseType nose_type;
105 u8 nose_scale;
106 u8 nose_y;
107 MouthType mouth_type;
108 CommonColor mouth_color;
109 u8 mouth_scale;
110 u8 mouth_aspect;
111 u8 mouth_y;
112 CommonColor beard_color;
113 BeardType beard_type;
114 MustacheType mustache_type;
115 u8 mustache_scale;
116 u8 mustache_y;
117 GlassType glass_type;
118 CommonColor glass_color;
119 u8 glass_scale;
120 u8 glass_y;
121 MoleType mole_type;
122 u8 mole_scale;
123 u8 mole_x;
124 u8 mole_y;
125 u8 padding;
126};
127static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
128static_assert(std::has_unique_object_representations_v<CharInfo>,
129 "All bits of CharInfo must contribute to its value.");
130
131struct CharInfoElement {
132 CharInfo char_info{};
133 Source source{};
134};
135static_assert(sizeof(CharInfoElement) == 0x5c, "CharInfoElement has incorrect size.");
136
137}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/core_data.cpp b/src/core/hle/service/mii/types/core_data.cpp
new file mode 100644
index 000000000..659288b51
--- /dev/null
+++ b/src/core/hle/service/mii/types/core_data.cpp
@@ -0,0 +1,601 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/hle/service/mii/mii_util.h"
6#include "core/hle/service/mii/types/core_data.h"
7#include "core/hle/service/mii/types/raw_data.h"
8
9namespace Service::Mii {
10
11void CoreData::SetDefault() {
12 data = {};
13 name = GetDefaultNickname();
14}
15
16void CoreData::BuildRandom(Age age, Gender gender, Race race) {
17 if (gender == Gender::All) {
18 gender = MiiUtil::GetRandomValue(Gender::Max);
19 }
20
21 if (age == Age::All) {
22 const auto random{MiiUtil::GetRandomValue<int>(10)};
23 if (random >= 8) {
24 age = Age::Old;
25 } else if (random >= 4) {
26 age = Age::Normal;
27 } else {
28 age = Age::Young;
29 }
30 }
31
32 if (race == Race::All) {
33 const auto random{MiiUtil::GetRandomValue<int>(10)};
34 if (random >= 8) {
35 race = Race::Black;
36 } else if (random >= 4) {
37 race = Race::White;
38 } else {
39 race = Race::Asian;
40 }
41 }
42
43 SetGender(gender);
44 SetFavoriteColor(MiiUtil::GetRandomValue(FavoriteColor::Max));
45 SetRegionMove(0);
46 SetFontRegion(FontRegion::Standard);
47 SetType(0);
48 SetHeight(64);
49 SetBuild(64);
50
51 u32 axis_y{};
52 if (gender == Gender::Female && age == Age::Young) {
53 axis_y = MiiUtil::GetRandomValue<u32>(3);
54 }
55
56 const std::size_t index{3 * static_cast<std::size_t>(age) +
57 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)};
58
59 const auto& faceline_type_info{RawData::RandomMiiFaceline.at(index)};
60 const auto& faceline_color_info{RawData::RandomMiiFacelineColor.at(
61 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))};
62 const auto& faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)};
63 const auto& faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)};
64 const auto& hair_type_info{RawData::RandomMiiHairType.at(index)};
65 const auto& hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) +
66 static_cast<std::size_t>(age))};
67 const auto& eye_type_info{RawData::RandomMiiEyeType.at(index)};
68 const auto& eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))};
69 const auto& eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)};
70 const auto& nose_type_info{RawData::RandomMiiNoseType.at(index)};
71 const auto& mouth_type_info{RawData::RandomMiiMouthType.at(index)};
72 const auto& glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))};
73
74 data.faceline_type.Assign(
75 faceline_type_info
76 .values[MiiUtil::GetRandomValue<std::size_t>(faceline_type_info.values_count)]);
77 data.faceline_color.Assign(
78 faceline_color_info
79 .values[MiiUtil::GetRandomValue<std::size_t>(faceline_color_info.values_count)]);
80 data.faceline_wrinkle.Assign(
81 faceline_wrinkle_info
82 .values[MiiUtil::GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]);
83 data.faceline_makeup.Assign(
84 faceline_makeup_info
85 .values[MiiUtil::GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]);
86
87 data.hair_type.Assign(
88 hair_type_info.values[MiiUtil::GetRandomValue<std::size_t>(hair_type_info.values_count)]);
89 SetHairColor(RawData::GetHairColorFromVer3(
90 hair_color_info
91 .values[MiiUtil::GetRandomValue<std::size_t>(hair_color_info.values_count)]));
92 SetHairFlip(MiiUtil::GetRandomValue(HairFlip::Max));
93
94 data.eye_type.Assign(
95 eye_type_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_type_info.values_count)]);
96
97 const auto eye_rotate_1{gender != Gender::Male ? 4 : 2};
98 const auto eye_rotate_2{gender != Gender::Male ? 3 : 4};
99 const auto eye_rotate_offset{32 - RawData::EyeRotateLookup[eye_rotate_1] + eye_rotate_2};
100 const auto eye_rotate{32 - RawData::EyeRotateLookup[data.eye_type]};
101
102 SetEyeColor(RawData::GetEyeColorFromVer3(
103 eye_color_info.values[MiiUtil::GetRandomValue<std::size_t>(eye_color_info.values_count)]));
104 SetEyeScale(4);
105 SetEyeAspect(3);
106 SetEyeRotate(static_cast<u8>(eye_rotate_offset - eye_rotate));
107 SetEyeX(2);
108 SetEyeY(static_cast<u8>(axis_y + 12));
109
110 data.eyebrow_type.Assign(
111 eyebrow_type_info
112 .values[MiiUtil::GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]);
113
114 const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0};
115 const auto eyebrow_y{race == Race::Asian ? 9 : 10};
116 const auto eyebrow_rotate_offset{32 - RawData::EyebrowRotateLookup[eyebrow_rotate_1] + 6};
117 const auto eyebrow_rotate{
118 32 - RawData::EyebrowRotateLookup[static_cast<std::size_t>(data.eyebrow_type.Value())]};
119
120 SetEyebrowColor(GetHairColor());
121 SetEyebrowScale(4);
122 SetEyebrowAspect(3);
123 SetEyebrowRotate(static_cast<u8>(eyebrow_rotate_offset - eyebrow_rotate));
124 SetEyebrowX(2);
125 SetEyebrowY(static_cast<u8>(axis_y + eyebrow_y));
126
127 data.nose_type.Assign(
128 nose_type_info.values[MiiUtil::GetRandomValue<std::size_t>(nose_type_info.values_count)]);
129 SetNoseScale(gender == Gender::Female ? 3 : 4);
130 SetNoseY(static_cast<u8>(axis_y + 9));
131
132 const auto mouth_color{gender == Gender::Female ? MiiUtil::GetRandomValue<int>(4) : 0};
133
134 data.mouth_type.Assign(
135 mouth_type_info.values[MiiUtil::GetRandomValue<std::size_t>(mouth_type_info.values_count)]);
136 SetMouthColor(RawData::GetMouthColorFromVer3(mouth_color));
137 SetMouthScale(4);
138 SetMouthAspect(3);
139 SetMouthY(static_cast<u8>(axis_y + 13));
140
141 SetBeardColor(GetHairColor());
142 SetMustacheScale(4);
143
144 if (gender == Gender::Male && age != Age::Young && MiiUtil::GetRandomValue<int>(10) < 2) {
145 const auto mustache_and_beard_flag{MiiUtil::GetRandomValue(BeardAndMustacheFlag::All)};
146
147 auto beard_type{BeardType::None};
148 auto mustache_type{MustacheType::None};
149
150 if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) ==
151 BeardAndMustacheFlag::Beard) {
152 beard_type = MiiUtil::GetRandomValue(BeardType::Min, BeardType::Max);
153 }
154
155 if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) ==
156 BeardAndMustacheFlag::Mustache) {
157 mustache_type = MiiUtil::GetRandomValue(MustacheType::Min, MustacheType::Max);
158 }
159
160 SetMustacheType(mustache_type);
161 SetBeardType(beard_type);
162 SetMustacheY(10);
163 } else {
164 SetMustacheType(MustacheType::None);
165 SetBeardType(BeardType::None);
166 SetMustacheY(static_cast<u8>(axis_y + 10));
167 }
168
169 const auto glasses_type_start{MiiUtil::GetRandomValue<std::size_t>(100)};
170 u8 glasses_type{};
171 while (glasses_type_start < glasses_type_info.values[glasses_type]) {
172 if (++glasses_type >= glasses_type_info.values_count) {
173 ASSERT(false);
174 break;
175 }
176 }
177
178 SetGlassType(static_cast<GlassType>(glasses_type));
179 SetGlassColor(RawData::GetGlassColorFromVer3(0));
180 SetGlassScale(4);
181
182 SetMoleType(MoleType::None);
183 SetMoleScale(4);
184 SetMoleX(2);
185 SetMoleY(20);
186}
187
188u32 CoreData::IsValid() const {
189 // TODO: Complete this
190 return 0;
191}
192
193void CoreData::SetFontRegion(FontRegion value) {
194 data.font_region.Assign(static_cast<u32>(value));
195}
196
197void CoreData::SetFavoriteColor(FavoriteColor value) {
198 data.favorite_color.Assign(static_cast<u32>(value));
199}
200
201void CoreData::SetGender(Gender value) {
202 data.gender.Assign(static_cast<u32>(value));
203}
204
205void CoreData::SetHeight(u8 value) {
206 data.height.Assign(value);
207}
208
209void CoreData::SetBuild(u8 value) {
210 data.build.Assign(value);
211}
212
213void CoreData::SetType(u8 value) {
214 data.type.Assign(value);
215}
216
217void CoreData::SetRegionMove(u8 value) {
218 data.region_move.Assign(value);
219}
220
221void CoreData::SetFacelineType(FacelineType value) {
222 data.faceline_type.Assign(static_cast<u32>(value));
223}
224
225void CoreData::SetFacelineColor(FacelineColor value) {
226 data.faceline_color.Assign(static_cast<u32>(value));
227}
228
229void CoreData::SetFacelineWrinkle(FacelineWrinkle value) {
230 data.faceline_wrinkle.Assign(static_cast<u32>(value));
231}
232
233void CoreData::SetFacelineMake(FacelineMake value) {
234 data.faceline_makeup.Assign(static_cast<u32>(value));
235}
236
237void CoreData::SetHairType(HairType value) {
238 data.hair_type.Assign(static_cast<u32>(value));
239}
240
241void CoreData::SetHairColor(CommonColor value) {
242 data.hair_color.Assign(static_cast<u32>(value));
243}
244
245void CoreData::SetHairFlip(HairFlip value) {
246 data.hair_flip.Assign(static_cast<u32>(value));
247}
248
249void CoreData::SetEyeType(EyeType value) {
250 data.eye_type.Assign(static_cast<u32>(value));
251}
252
253void CoreData::SetEyeColor(CommonColor value) {
254 data.eye_color.Assign(static_cast<u32>(value));
255}
256
257void CoreData::SetEyeScale(u8 value) {
258 data.eye_scale.Assign(value);
259}
260
261void CoreData::SetEyeAspect(u8 value) {
262 data.eye_aspect.Assign(value);
263}
264
265void CoreData::SetEyeRotate(u8 value) {
266 data.eye_rotate.Assign(value);
267}
268
269void CoreData::SetEyeX(u8 value) {
270 data.eye_x.Assign(value);
271}
272
273void CoreData::SetEyeY(u8 value) {
274 data.eye_y.Assign(value);
275}
276
277void CoreData::SetEyebrowType(EyebrowType value) {
278 data.eyebrow_type.Assign(static_cast<u32>(value));
279}
280
281void CoreData::SetEyebrowColor(CommonColor value) {
282 data.eyebrow_color.Assign(static_cast<u32>(value));
283}
284
285void CoreData::SetEyebrowScale(u8 value) {
286 data.eyebrow_scale.Assign(value);
287}
288
289void CoreData::SetEyebrowAspect(u8 value) {
290 data.eyebrow_aspect.Assign(value);
291}
292
293void CoreData::SetEyebrowRotate(u8 value) {
294 data.eyebrow_rotate.Assign(value);
295}
296
297void CoreData::SetEyebrowX(u8 value) {
298 data.eyebrow_x.Assign(value);
299}
300
301void CoreData::SetEyebrowY(u8 value) {
302 data.eyebrow_y.Assign(value);
303}
304
305void CoreData::SetNoseType(NoseType value) {
306 data.nose_type.Assign(static_cast<u32>(value));
307}
308
309void CoreData::SetNoseScale(u8 value) {
310 data.nose_scale.Assign(value);
311}
312
313void CoreData::SetNoseY(u8 value) {
314 data.nose_y.Assign(value);
315}
316
317void CoreData::SetMouthType(u8 value) {
318 data.mouth_type.Assign(value);
319}
320
321void CoreData::SetMouthColor(CommonColor value) {
322 data.mouth_color.Assign(static_cast<u32>(value));
323}
324
325void CoreData::SetMouthScale(u8 value) {
326 data.mouth_scale.Assign(value);
327}
328
329void CoreData::SetMouthAspect(u8 value) {
330 data.mouth_aspect.Assign(value);
331}
332
333void CoreData::SetMouthY(u8 value) {
334 data.mouth_y.Assign(value);
335}
336
337void CoreData::SetBeardColor(CommonColor value) {
338 data.beard_color.Assign(static_cast<u32>(value));
339}
340
341void CoreData::SetBeardType(BeardType value) {
342 data.beard_type.Assign(static_cast<u32>(value));
343}
344
345void CoreData::SetMustacheType(MustacheType value) {
346 data.mustache_type.Assign(static_cast<u32>(value));
347}
348
349void CoreData::SetMustacheScale(u8 value) {
350 data.mustache_scale.Assign(value);
351}
352
353void CoreData::SetMustacheY(u8 value) {
354 data.mustache_y.Assign(value);
355}
356
357void CoreData::SetGlassType(GlassType value) {
358 data.glasses_type.Assign(static_cast<u32>(value));
359}
360
361void CoreData::SetGlassColor(CommonColor value) {
362 data.glasses_color.Assign(static_cast<u32>(value));
363}
364
365void CoreData::SetGlassScale(u8 value) {
366 data.glasses_scale.Assign(value);
367}
368
369void CoreData::SetGlassY(u8 value) {
370 data.glasses_y.Assign(value);
371}
372
373void CoreData::SetMoleType(MoleType value) {
374 data.mole_type.Assign(static_cast<u32>(value));
375}
376
377void CoreData::SetMoleScale(u8 value) {
378 data.mole_scale.Assign(value);
379}
380
381void CoreData::SetMoleX(u8 value) {
382 data.mole_x.Assign(value);
383}
384
385void CoreData::SetMoleY(u8 value) {
386 data.mole_y.Assign(value);
387}
388
389void CoreData::SetNickname(Nickname nickname) {
390 name = nickname;
391}
392
393FontRegion CoreData::GetFontRegion() const {
394 return static_cast<FontRegion>(data.font_region.Value());
395}
396
397FavoriteColor CoreData::GetFavoriteColor() const {
398 return static_cast<FavoriteColor>(data.favorite_color.Value());
399}
400
401Gender CoreData::GetGender() const {
402 return static_cast<Gender>(data.gender.Value());
403}
404
405u8 CoreData::GetHeight() const {
406 return static_cast<u8>(data.height.Value());
407}
408
409u8 CoreData::GetBuild() const {
410 return static_cast<u8>(data.build.Value());
411}
412
413u8 CoreData::GetType() const {
414 return static_cast<u8>(data.type.Value());
415}
416
417u8 CoreData::GetRegionMove() const {
418 return static_cast<u8>(data.region_move.Value());
419}
420
421FacelineType CoreData::GetFacelineType() const {
422 return static_cast<FacelineType>(data.faceline_type.Value());
423}
424
425FacelineColor CoreData::GetFacelineColor() const {
426 return static_cast<FacelineColor>(data.faceline_color.Value());
427}
428
429FacelineWrinkle CoreData::GetFacelineWrinkle() const {
430 return static_cast<FacelineWrinkle>(data.faceline_wrinkle.Value());
431}
432
433FacelineMake CoreData::GetFacelineMake() const {
434 return static_cast<FacelineMake>(data.faceline_makeup.Value());
435}
436
437HairType CoreData::GetHairType() const {
438 return static_cast<HairType>(data.hair_type.Value());
439}
440
441CommonColor CoreData::GetHairColor() const {
442 return static_cast<CommonColor>(data.hair_color.Value());
443}
444
445HairFlip CoreData::GetHairFlip() const {
446 return static_cast<HairFlip>(data.hair_flip.Value());
447}
448
449EyeType CoreData::GetEyeType() const {
450 return static_cast<EyeType>(data.eye_type.Value());
451}
452
453CommonColor CoreData::GetEyeColor() const {
454 return static_cast<CommonColor>(data.eye_color.Value());
455}
456
457u8 CoreData::GetEyeScale() const {
458 return static_cast<u8>(data.eye_scale.Value());
459}
460
461u8 CoreData::GetEyeAspect() const {
462 return static_cast<u8>(data.eye_aspect.Value());
463}
464
465u8 CoreData::GetEyeRotate() const {
466 return static_cast<u8>(data.eye_rotate.Value());
467}
468
469u8 CoreData::GetEyeX() const {
470 return static_cast<u8>(data.eye_x.Value());
471}
472
473u8 CoreData::GetEyeY() const {
474 return static_cast<u8>(data.eye_y.Value());
475}
476
477EyebrowType CoreData::GetEyebrowType() const {
478 return static_cast<EyebrowType>(data.eyebrow_type.Value());
479}
480
481CommonColor CoreData::GetEyebrowColor() const {
482 return static_cast<CommonColor>(data.eyebrow_color.Value());
483}
484
485u8 CoreData::GetEyebrowScale() const {
486 return static_cast<u8>(data.eyebrow_scale.Value());
487}
488
489u8 CoreData::GetEyebrowAspect() const {
490 return static_cast<u8>(data.eyebrow_aspect.Value());
491}
492
493u8 CoreData::GetEyebrowRotate() const {
494 return static_cast<u8>(data.eyebrow_rotate.Value());
495}
496
497u8 CoreData::GetEyebrowX() const {
498 return static_cast<u8>(data.eyebrow_x.Value());
499}
500
501u8 CoreData::GetEyebrowY() const {
502 return static_cast<u8>(data.eyebrow_y.Value());
503}
504
505NoseType CoreData::GetNoseType() const {
506 return static_cast<NoseType>(data.nose_type.Value());
507}
508
509u8 CoreData::GetNoseScale() const {
510 return static_cast<u8>(data.nose_scale.Value());
511}
512
513u8 CoreData::GetNoseY() const {
514 return static_cast<u8>(data.nose_y.Value());
515}
516
517MouthType CoreData::GetMouthType() const {
518 return static_cast<MouthType>(data.mouth_type.Value());
519}
520
521CommonColor CoreData::GetMouthColor() const {
522 return static_cast<CommonColor>(data.mouth_color.Value());
523}
524
525u8 CoreData::GetMouthScale() const {
526 return static_cast<u8>(data.mouth_scale.Value());
527}
528
529u8 CoreData::GetMouthAspect() const {
530 return static_cast<u8>(data.mouth_aspect.Value());
531}
532
533u8 CoreData::GetMouthY() const {
534 return static_cast<u8>(data.mouth_y.Value());
535}
536
537CommonColor CoreData::GetBeardColor() const {
538 return static_cast<CommonColor>(data.beard_color.Value());
539}
540
541BeardType CoreData::GetBeardType() const {
542 return static_cast<BeardType>(data.beard_type.Value());
543}
544
545MustacheType CoreData::GetMustacheType() const {
546 return static_cast<MustacheType>(data.mustache_type.Value());
547}
548
549u8 CoreData::GetMustacheScale() const {
550 return static_cast<u8>(data.mustache_scale.Value());
551}
552
553u8 CoreData::GetMustacheY() const {
554 return static_cast<u8>(data.mustache_y.Value());
555}
556
557GlassType CoreData::GetGlassType() const {
558 return static_cast<GlassType>(data.glasses_type.Value());
559}
560
561CommonColor CoreData::GetGlassColor() const {
562 return static_cast<CommonColor>(data.glasses_color.Value());
563}
564
565u8 CoreData::GetGlassScale() const {
566 return static_cast<u8>(data.glasses_scale.Value());
567}
568
569u8 CoreData::GetGlassY() const {
570 return static_cast<u8>(data.glasses_y.Value());
571}
572
573MoleType CoreData::GetMoleType() const {
574 return static_cast<MoleType>(data.mole_type.Value());
575}
576
577u8 CoreData::GetMoleScale() const {
578 return static_cast<u8>(data.mole_scale.Value());
579}
580
581u8 CoreData::GetMoleX() const {
582 return static_cast<u8>(data.mole_x.Value());
583}
584
585u8 CoreData::GetMoleY() const {
586 return static_cast<u8>(data.mole_y.Value());
587}
588
589Nickname CoreData::GetNickname() const {
590 return name;
591}
592
593Nickname CoreData::GetDefaultNickname() const {
594 return {u'n', u'o', u' ', u'n', u'a', u'm', u'e'};
595}
596
597Nickname CoreData::GetInvalidNickname() const {
598 return {u'?', u'?', u'?'};
599}
600
601} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/core_data.h b/src/core/hle/service/mii/types/core_data.h
new file mode 100644
index 000000000..cebcd2ee4
--- /dev/null
+++ b/src/core/hle/service/mii/types/core_data.h
@@ -0,0 +1,216 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/mii/mii_types.h"
7
8namespace Service::Mii {
9
10struct StoreDataBitFields {
11 union {
12 u32 word_0{};
13
14 BitField<0, 8, u32> hair_type;
15 BitField<8, 7, u32> height;
16 BitField<15, 1, u32> mole_type;
17 BitField<16, 7, u32> build;
18 BitField<23, 1, u32> hair_flip;
19 BitField<24, 7, u32> hair_color;
20 BitField<31, 1, u32> type;
21 };
22
23 union {
24 u32 word_1{};
25
26 BitField<0, 7, u32> eye_color;
27 BitField<7, 1, u32> gender;
28 BitField<8, 7, u32> eyebrow_color;
29 BitField<16, 7, u32> mouth_color;
30 BitField<24, 7, u32> beard_color;
31 };
32
33 union {
34 u32 word_2{};
35
36 BitField<0, 7, u32> glasses_color;
37 BitField<8, 6, u32> eye_type;
38 BitField<14, 2, u32> region_move;
39 BitField<16, 6, u32> mouth_type;
40 BitField<22, 2, u32> font_region;
41 BitField<24, 5, u32> eye_y;
42 BitField<29, 3, u32> glasses_scale;
43 };
44
45 union {
46 u32 word_3{};
47
48 BitField<0, 5, u32> eyebrow_type;
49 BitField<5, 3, u32> mustache_type;
50 BitField<8, 5, u32> nose_type;
51 BitField<13, 3, u32> beard_type;
52 BitField<16, 5, u32> nose_y;
53 BitField<21, 3, u32> mouth_aspect;
54 BitField<24, 5, u32> mouth_y;
55 BitField<29, 3, u32> eyebrow_aspect;
56 };
57
58 union {
59 u32 word_4{};
60
61 BitField<0, 5, u32> mustache_y;
62 BitField<5, 3, u32> eye_rotate;
63 BitField<8, 5, u32> glasses_y;
64 BitField<13, 3, u32> eye_aspect;
65 BitField<16, 5, u32> mole_x;
66 BitField<21, 3, u32> eye_scale;
67 BitField<24, 5, u32> mole_y;
68 };
69
70 union {
71 u32 word_5{};
72
73 BitField<0, 5, u32> glasses_type;
74 BitField<8, 4, u32> favorite_color;
75 BitField<12, 4, u32> faceline_type;
76 BitField<16, 4, u32> faceline_color;
77 BitField<20, 4, u32> faceline_wrinkle;
78 BitField<24, 4, u32> faceline_makeup;
79 BitField<28, 4, u32> eye_x;
80 };
81
82 union {
83 u32 word_6{};
84
85 BitField<0, 4, u32> eyebrow_scale;
86 BitField<4, 4, u32> eyebrow_rotate;
87 BitField<8, 4, u32> eyebrow_x;
88 BitField<12, 4, u32> eyebrow_y;
89 BitField<16, 4, u32> nose_scale;
90 BitField<20, 4, u32> mouth_scale;
91 BitField<24, 4, u32> mustache_scale;
92 BitField<28, 4, u32> mole_scale;
93 };
94};
95static_assert(sizeof(StoreDataBitFields) == 0x1c, "StoreDataBitFields has incorrect size.");
96static_assert(std::is_trivially_copyable_v<StoreDataBitFields>,
97 "StoreDataBitFields is not trivially copyable.");
98
99class CoreData {
100public:
101 void SetDefault();
102 void BuildRandom(Age age, Gender gender, Race race);
103
104 u32 IsValid() const;
105
106 void SetFontRegion(FontRegion value);
107 void SetFavoriteColor(FavoriteColor value);
108 void SetGender(Gender value);
109 void SetHeight(u8 value);
110 void SetBuild(u8 value);
111 void SetType(u8 value);
112 void SetRegionMove(u8 value);
113 void SetFacelineType(FacelineType value);
114 void SetFacelineColor(FacelineColor value);
115 void SetFacelineWrinkle(FacelineWrinkle value);
116 void SetFacelineMake(FacelineMake value);
117 void SetHairType(HairType value);
118 void SetHairColor(CommonColor value);
119 void SetHairFlip(HairFlip value);
120 void SetEyeType(EyeType value);
121 void SetEyeColor(CommonColor value);
122 void SetEyeScale(u8 value);
123 void SetEyeAspect(u8 value);
124 void SetEyeRotate(u8 value);
125 void SetEyeX(u8 value);
126 void SetEyeY(u8 value);
127 void SetEyebrowType(EyebrowType value);
128 void SetEyebrowColor(CommonColor value);
129 void SetEyebrowScale(u8 value);
130 void SetEyebrowAspect(u8 value);
131 void SetEyebrowRotate(u8 value);
132 void SetEyebrowX(u8 value);
133 void SetEyebrowY(u8 value);
134 void SetNoseType(NoseType value);
135 void SetNoseScale(u8 value);
136 void SetNoseY(u8 value);
137 void SetMouthType(u8 value);
138 void SetMouthColor(CommonColor value);
139 void SetMouthScale(u8 value);
140 void SetMouthAspect(u8 value);
141 void SetMouthY(u8 value);
142 void SetBeardColor(CommonColor value);
143 void SetBeardType(BeardType value);
144 void SetMustacheType(MustacheType value);
145 void SetMustacheScale(u8 value);
146 void SetMustacheY(u8 value);
147 void SetGlassType(GlassType value);
148 void SetGlassColor(CommonColor value);
149 void SetGlassScale(u8 value);
150 void SetGlassY(u8 value);
151 void SetMoleType(MoleType value);
152 void SetMoleScale(u8 value);
153 void SetMoleX(u8 value);
154 void SetMoleY(u8 value);
155 void SetNickname(Nickname nickname);
156
157 FontRegion GetFontRegion() const;
158 FavoriteColor GetFavoriteColor() const;
159 Gender GetGender() const;
160 u8 GetHeight() const;
161 u8 GetBuild() const;
162 u8 GetType() const;
163 u8 GetRegionMove() const;
164 FacelineType GetFacelineType() const;
165 FacelineColor GetFacelineColor() const;
166 FacelineWrinkle GetFacelineWrinkle() const;
167 FacelineMake GetFacelineMake() const;
168 HairType GetHairType() const;
169 CommonColor GetHairColor() const;
170 HairFlip GetHairFlip() const;
171 EyeType GetEyeType() const;
172 CommonColor GetEyeColor() const;
173 u8 GetEyeScale() const;
174 u8 GetEyeAspect() const;
175 u8 GetEyeRotate() const;
176 u8 GetEyeX() const;
177 u8 GetEyeY() const;
178 EyebrowType GetEyebrowType() const;
179 CommonColor GetEyebrowColor() const;
180 u8 GetEyebrowScale() const;
181 u8 GetEyebrowAspect() const;
182 u8 GetEyebrowRotate() const;
183 u8 GetEyebrowX() const;
184 u8 GetEyebrowY() const;
185 NoseType GetNoseType() const;
186 u8 GetNoseScale() const;
187 u8 GetNoseY() const;
188 MouthType GetMouthType() const;
189 CommonColor GetMouthColor() const;
190 u8 GetMouthScale() const;
191 u8 GetMouthAspect() const;
192 u8 GetMouthY() const;
193 CommonColor GetBeardColor() const;
194 BeardType GetBeardType() const;
195 MustacheType GetMustacheType() const;
196 u8 GetMustacheScale() const;
197 u8 GetMustacheY() const;
198 GlassType GetGlassType() const;
199 CommonColor GetGlassColor() const;
200 u8 GetGlassScale() const;
201 u8 GetGlassY() const;
202 MoleType GetMoleType() const;
203 u8 GetMoleScale() const;
204 u8 GetMoleX() const;
205 u8 GetMoleY() const;
206 Nickname GetNickname() const;
207 Nickname GetDefaultNickname() const;
208 Nickname GetInvalidNickname() const;
209
210private:
211 StoreDataBitFields data{};
212 Nickname name{};
213};
214static_assert(sizeof(CoreData) == 0x30, "CoreData has incorrect size.");
215
216}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/types/raw_data.cpp
index 1442280c8..5143abcc8 100644
--- a/src/core/hle/service/mii/raw_data.cpp
+++ b/src/core/hle/service/mii/types/raw_data.cpp
@@ -1,11 +1,88 @@
1// SPDX-FileCopyrightText: Ryujinx Team and Contributors 1// SPDX-FileCopyrightText: Ryujinx Team and Contributors
2// SPDX-License-Identifier: MIT 2// SPDX-License-Identifier: MIT
3 3
4#include "core/hle/service/mii/raw_data.h" 4#include "core/hle/service/mii/types/raw_data.h"
5 5
6namespace Service::Mii::RawData { 6namespace Service::Mii::RawData {
7 7
8const std::array<Service::Mii::DefaultMii, 8> DefaultMii{ 8constexpr std::array<u8, static_cast<u8>(FacelineColor::Count)> FromVer3FacelineColorTable{
9 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x0, 0x1, 0x5, 0x5,
10};
11
12constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3HairColorTable{
13 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x0, 0x4, 0x3, 0x5, 0x4, 0x4, 0x6, 0x2, 0x0,
14 0x6, 0x4, 0x3, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
15 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x4,
16 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5,
17 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x7, 0x5, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x7,
18 0x7, 0x7, 0x7, 0x7, 0x3, 0x7, 0x7, 0x7, 0x7, 0x7, 0x0, 0x4, 0x4, 0x4, 0x4,
19};
20
21constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3EyeColorTable{
22 0x0, 0x2, 0x2, 0x2, 0x1, 0x3, 0x2, 0x3, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x2, 0x2, 0x4,
23 0x2, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
24 0x2, 0x2, 0x2, 0x2, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x0, 0x4, 0x4,
25 0x4, 0x4, 0x4, 0x4, 0x4, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
26 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2,
27 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
28};
29
30constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3MouthlineColorTable{
31 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
32 0x4, 0x4, 0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x1, 0x4,
33 0x4, 0x2, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
34 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4,
35 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3,
36 0x3, 0x3, 0x3, 0x3, 0x4, 0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, 0x3, 0x3,
37};
38
39constexpr std::array<u8, static_cast<u8>(CommonColor::Count)> FromVer3GlassColorTable{
40 0x0, 0x1, 0x1, 0x1, 0x5, 0x1, 0x1, 0x4, 0x0, 0x5, 0x1, 0x1, 0x3, 0x5, 0x1, 0x2, 0x3,
41 0x4, 0x5, 0x4, 0x2, 0x2, 0x4, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
42 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
43 0x3, 0x3, 0x3, 0x3, 0x3, 0x0, 0x0, 0x0, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x0, 0x5, 0x5,
44 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x1, 0x4,
45 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5,
46};
47
48constexpr std::array<u8, static_cast<u8>(GlassType::Count)> FromVer3GlassTypeTable{
49 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x1,
50 0x2, 0x1, 0x3, 0x7, 0x7, 0x6, 0x7, 0x8, 0x7, 0x7,
51};
52
53constexpr std::array<u8, 8> Ver3FacelineColorTable{
54 0x0, 0x1, 0x2, 0x3, 0x4, 0x5,
55};
56
57constexpr std::array<u8, 8> Ver3HairColorTable{
58 0x8, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
59};
60
61constexpr std::array<u8, 6> Ver3EyeColorTable{
62 0x8, 0x9, 0xa, 0xb, 0xc, 0xd,
63};
64
65constexpr std::array<u8, 5> Ver3MouthColorTable{
66 0x13, 0x14, 0x15, 0x16, 0x17,
67};
68
69constexpr std::array<u8, 7> Ver3GlassColorTable{
70 0x8, 0xe, 0xf, 0x10, 0x11, 0x12, 0x0,
71};
72
73const std::array<u8, 62> EyeRotateLookup{
74 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04,
75 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
76 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03,
77 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04,
78};
79
80const std::array<u8, 24> EyebrowRotateLookup{
81 0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, 0x04, 0x07, 0x06, 0x08,
82 0x05, 0x05, 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05,
83};
84
85const std::array<Service::Mii::DefaultMii, 2> BaseMii{
9 Service::Mii::DefaultMii{ 86 Service::Mii::DefaultMii{
10 .face_type = 0, 87 .face_type = 0,
11 .face_color = 0, 88 .face_color = 0,
@@ -51,11 +128,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
51 .mole_y = 20, 128 .mole_y = 20,
52 .height = 64, 129 .height = 64,
53 .weight = 64, 130 .weight = 64,
54 .gender = Gender::Male, 131 .gender = 0,
55 .favorite_color = 0, 132 .favorite_color = 0,
56 .region = 0, 133 .region_move = 0,
57 .font_region = FontRegion::Standard, 134 .font_region = 0,
58 .type = 0, 135 .type = 0,
136 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
59 }, 137 },
60 Service::Mii::DefaultMii{ 138 Service::Mii::DefaultMii{
61 .face_type = 0, 139 .face_type = 0,
@@ -102,12 +180,16 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
102 .mole_y = 20, 180 .mole_y = 20,
103 .height = 64, 181 .height = 64,
104 .weight = 64, 182 .weight = 64,
105 .gender = Gender::Female, 183 .gender = 1,
106 .favorite_color = 0, 184 .favorite_color = 0,
107 .region = 0, 185 .region_move = 0,
108 .font_region = FontRegion::Standard, 186 .font_region = 0,
109 .type = 0, 187 .type = 0,
188 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
110 }, 189 },
190};
191
192const std::array<Service::Mii::DefaultMii, 6> DefaultMii{
111 Service::Mii::DefaultMii{ 193 Service::Mii::DefaultMii{
112 .face_type = 0, 194 .face_type = 0,
113 .face_color = 4, 195 .face_color = 4,
@@ -153,11 +235,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
153 .mole_y = 20, 235 .mole_y = 20,
154 .height = 64, 236 .height = 64,
155 .weight = 64, 237 .weight = 64,
156 .gender = Gender::Male, 238 .gender = 0,
157 .favorite_color = 4, 239 .favorite_color = 4,
158 .region = 0, 240 .region_move = 0,
159 .font_region = FontRegion::Standard, 241 .font_region = 0,
160 .type = 0, 242 .type = 0,
243 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
161 }, 244 },
162 Service::Mii::DefaultMii{ 245 Service::Mii::DefaultMii{
163 .face_type = 0, 246 .face_type = 0,
@@ -204,11 +287,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
204 .mole_y = 20, 287 .mole_y = 20,
205 .height = 64, 288 .height = 64,
206 .weight = 64, 289 .weight = 64,
207 .gender = Gender::Male, 290 .gender = 0,
208 .favorite_color = 5, 291 .favorite_color = 5,
209 .region = 0, 292 .region_move = 0,
210 .font_region = FontRegion::Standard, 293 .font_region = 0,
211 .type = 0, 294 .type = 0,
295 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
212 }, 296 },
213 Service::Mii::DefaultMii{ 297 Service::Mii::DefaultMii{
214 .face_type = 0, 298 .face_type = 0,
@@ -255,11 +339,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
255 .mole_y = 20, 339 .mole_y = 20,
256 .height = 64, 340 .height = 64,
257 .weight = 64, 341 .weight = 64,
258 .gender = Gender::Male, 342 .gender = 0,
259 .favorite_color = 0, 343 .favorite_color = 0,
260 .region = 0, 344 .region_move = 0,
261 .font_region = FontRegion::Standard, 345 .font_region = 0,
262 .type = 0, 346 .type = 0,
347 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
263 }, 348 },
264 Service::Mii::DefaultMii{ 349 Service::Mii::DefaultMii{
265 .face_type = 0, 350 .face_type = 0,
@@ -306,11 +391,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
306 .mole_y = 20, 391 .mole_y = 20,
307 .height = 64, 392 .height = 64,
308 .weight = 64, 393 .weight = 64,
309 .gender = Gender::Female, 394 .gender = 1,
310 .favorite_color = 2, 395 .favorite_color = 2,
311 .region = 0, 396 .region_move = 0,
312 .font_region = FontRegion::Standard, 397 .font_region = 0,
313 .type = 0, 398 .type = 0,
399 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
314 }, 400 },
315 Service::Mii::DefaultMii{ 401 Service::Mii::DefaultMii{
316 .face_type = 0, 402 .face_type = 0,
@@ -357,11 +443,12 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
357 .mole_y = 20, 443 .mole_y = 20,
358 .height = 64, 444 .height = 64,
359 .weight = 64, 445 .weight = 64,
360 .gender = Gender::Female, 446 .gender = 1,
361 .favorite_color = 6, 447 .favorite_color = 6,
362 .region = 0, 448 .region_move = 0,
363 .font_region = FontRegion::Standard, 449 .font_region = 0,
364 .type = 0, 450 .type = 0,
451 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
365 }, 452 },
366 Service::Mii::DefaultMii{ 453 Service::Mii::DefaultMii{
367 .face_type = 0, 454 .face_type = 0,
@@ -408,176 +495,177 @@ const std::array<Service::Mii::DefaultMii, 8> DefaultMii{
408 .mole_y = 20, 495 .mole_y = 20,
409 .height = 64, 496 .height = 64,
410 .weight = 64, 497 .weight = 64,
411 .gender = Gender::Female, 498 .gender = 1,
412 .favorite_color = 7, 499 .favorite_color = 7,
413 .region = 0, 500 .region_move = 0,
414 .font_region = FontRegion::Standard, 501 .font_region = 0,
415 .type = 0, 502 .type = 0,
503 .nickname = {u'n', u'o', u' ', u'n', u'a', u'm', u'e'},
416 }, 504 },
417 505
418}; 506};
419 507
420const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFaceline{ 508const std::array<RandomMiiData4, 18> RandomMiiFaceline{
421 Service::Mii::RandomMiiData4{ 509 RandomMiiData4{
422 .gender = Gender::Male, 510 .gender = static_cast<u32>(Gender::Male),
423 .age = Age::Young, 511 .age = static_cast<u32>(Age::Young),
424 .race = Race::Black, 512 .race = static_cast<u32>(Race::Black),
425 .values_count = 10, 513 .values_count = 10,
426 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, 514 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
427 }, 515 },
428 Service::Mii::RandomMiiData4{ 516 RandomMiiData4{
429 .gender = Gender::Male, 517 .gender = static_cast<u32>(Gender::Male),
430 .age = Age::Normal, 518 .age = static_cast<u32>(Age::Normal),
431 .race = Race::Black, 519 .race = static_cast<u32>(Race::Black),
432 .values_count = 10, 520 .values_count = 10,
433 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, 521 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
434 }, 522 },
435 Service::Mii::RandomMiiData4{ 523 RandomMiiData4{
436 .gender = Gender::Male, 524 .gender = static_cast<u32>(Gender::Male),
437 .age = Age::Old, 525 .age = static_cast<u32>(Age::Old),
438 .race = Race::Black, 526 .race = static_cast<u32>(Race::Black),
439 .values_count = 10, 527 .values_count = 10,
440 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, 528 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
441 }, 529 },
442 Service::Mii::RandomMiiData4{ 530 RandomMiiData4{
443 .gender = Gender::Male, 531 .gender = static_cast<u32>(Gender::Male),
444 .age = Age::Young, 532 .age = static_cast<u32>(Age::Young),
445 .race = Race::White, 533 .race = static_cast<u32>(Race::White),
446 .values_count = 12, 534 .values_count = 12,
447 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, 535 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
448 }, 536 },
449 Service::Mii::RandomMiiData4{ 537 RandomMiiData4{
450 .gender = Gender::Male, 538 .gender = static_cast<u32>(Gender::Male),
451 .age = Age::Normal, 539 .age = static_cast<u32>(Age::Normal),
452 .race = Race::White, 540 .race = static_cast<u32>(Race::White),
453 .values_count = 13, 541 .values_count = 13,
454 .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, 542 .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11},
455 }, 543 },
456 Service::Mii::RandomMiiData4{ 544 RandomMiiData4{
457 .gender = Gender::Male, 545 .gender = static_cast<u32>(Gender::Male),
458 .age = Age::Old, 546 .age = static_cast<u32>(Age::Old),
459 .race = Race::White, 547 .race = static_cast<u32>(Race::White),
460 .values_count = 12, 548 .values_count = 12,
461 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, 549 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
462 }, 550 },
463 Service::Mii::RandomMiiData4{ 551 RandomMiiData4{
464 .gender = Gender::Male, 552 .gender = static_cast<u32>(Gender::Male),
465 .age = Age::Young, 553 .age = static_cast<u32>(Age::Young),
466 .race = Race::Asian, 554 .race = static_cast<u32>(Race::Asian),
467 .values_count = 12, 555 .values_count = 12,
468 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, 556 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
469 }, 557 },
470 Service::Mii::RandomMiiData4{ 558 RandomMiiData4{
471 .gender = Gender::Male, 559 .gender = static_cast<u32>(Gender::Male),
472 .age = Age::Normal, 560 .age = static_cast<u32>(Age::Normal),
473 .race = Race::Asian, 561 .race = static_cast<u32>(Race::Asian),
474 .values_count = 13, 562 .values_count = 13,
475 .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11}, 563 .values = {0, 1, 2, 2, 3, 4, 5, 6, 6, 7, 7, 10, 11},
476 }, 564 },
477 Service::Mii::RandomMiiData4{ 565 RandomMiiData4{
478 .gender = Gender::Male, 566 .gender = static_cast<u32>(Gender::Male),
479 .age = Age::Old, 567 .age = static_cast<u32>(Age::Old),
480 .race = Race::Asian, 568 .race = static_cast<u32>(Race::Asian),
481 .values_count = 12, 569 .values_count = 12,
482 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11}, 570 .values = {0, 0, 1, 2, 2, 3, 4, 5, 6, 7, 10, 11},
483 }, 571 },
484 Service::Mii::RandomMiiData4{ 572 RandomMiiData4{
485 .gender = Gender::Female, 573 .gender = static_cast<u32>(Gender::Female),
486 .age = Age::Young, 574 .age = static_cast<u32>(Age::Young),
487 .race = Race::Black, 575 .race = static_cast<u32>(Race::Black),
488 .values_count = 10, 576 .values_count = 10,
489 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, 577 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
490 }, 578 },
491 Service::Mii::RandomMiiData4{ 579 RandomMiiData4{
492 .gender = Gender::Female, 580 .gender = static_cast<u32>(Gender::Female),
493 .age = Age::Normal, 581 .age = static_cast<u32>(Age::Normal),
494 .race = Race::Black, 582 .race = static_cast<u32>(Race::Black),
495 .values_count = 10, 583 .values_count = 10,
496 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, 584 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
497 }, 585 },
498 Service::Mii::RandomMiiData4{ 586 RandomMiiData4{
499 .gender = Gender::Female, 587 .gender = static_cast<u32>(Gender::Female),
500 .age = Age::Old, 588 .age = static_cast<u32>(Age::Old),
501 .race = Race::Black, 589 .race = static_cast<u32>(Race::Black),
502 .values_count = 10, 590 .values_count = 10,
503 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9}, 591 .values = {0, 0, 1, 1, 2, 3, 4, 5, 9, 9},
504 }, 592 },
505 Service::Mii::RandomMiiData4{ 593 RandomMiiData4{
506 .gender = Gender::Female, 594 .gender = static_cast<u32>(Gender::Female),
507 .age = Age::Young, 595 .age = static_cast<u32>(Age::Young),
508 .race = Race::White, 596 .race = static_cast<u32>(Race::White),
509 .values_count = 12, 597 .values_count = 12,
510 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, 598 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
511 }, 599 },
512 Service::Mii::RandomMiiData4{ 600 RandomMiiData4{
513 .gender = Gender::Female, 601 .gender = static_cast<u32>(Gender::Female),
514 .age = Age::Normal, 602 .age = static_cast<u32>(Age::Normal),
515 .race = Race::White, 603 .race = static_cast<u32>(Race::White),
516 .values_count = 12, 604 .values_count = 12,
517 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, 605 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
518 }, 606 },
519 Service::Mii::RandomMiiData4{ 607 RandomMiiData4{
520 .gender = Gender::Female, 608 .gender = static_cast<u32>(Gender::Female),
521 .age = Age::Old, 609 .age = static_cast<u32>(Age::Old),
522 .race = Race::White, 610 .race = static_cast<u32>(Race::White),
523 .values_count = 12, 611 .values_count = 12,
524 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, 612 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
525 }, 613 },
526 Service::Mii::RandomMiiData4{ 614 RandomMiiData4{
527 .gender = Gender::Female, 615 .gender = static_cast<u32>(Gender::Female),
528 .age = Age::Young, 616 .age = static_cast<u32>(Age::Young),
529 .race = Race::Asian, 617 .race = static_cast<u32>(Race::Asian),
530 .values_count = 12, 618 .values_count = 12,
531 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, 619 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
532 }, 620 },
533 Service::Mii::RandomMiiData4{ 621 RandomMiiData4{
534 .gender = Gender::Female, 622 .gender = static_cast<u32>(Gender::Female),
535 .age = Age::Normal, 623 .age = static_cast<u32>(Age::Normal),
536 .race = Race::Asian, 624 .race = static_cast<u32>(Race::Asian),
537 .values_count = 12, 625 .values_count = 12,
538 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, 626 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
539 }, 627 },
540 Service::Mii::RandomMiiData4{ 628 RandomMiiData4{
541 .gender = Gender::Female, 629 .gender = static_cast<u32>(Gender::Female),
542 .age = Age::Old, 630 .age = static_cast<u32>(Age::Old),
543 .race = Race::Asian, 631 .race = static_cast<u32>(Race::Asian),
544 .values_count = 12, 632 .values_count = 12,
545 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10}, 633 .values = {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 8, 10},
546 }, 634 },
547}; 635};
548 636
549const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{ 637const std::array<RandomMiiData3, 6> RandomMiiFacelineColor{
550 Service::Mii::RandomMiiData3{ 638 RandomMiiData3{
551 .arg_1 = 0, 639 .arg_1 = 0,
552 .arg_2 = 0, 640 .arg_2 = 0,
553 .values_count = 10, 641 .values_count = 10,
554 .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, 642 .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5},
555 }, 643 },
556 Service::Mii::RandomMiiData3{ 644 RandomMiiData3{
557 .arg_1 = 0, 645 .arg_1 = 0,
558 .arg_2 = 1, 646 .arg_2 = 1,
559 .values_count = 10, 647 .values_count = 10,
560 .values = {0, 0, 0, 0, 1, 1, 2, 3, 3, 3}, 648 .values = {0, 0, 0, 0, 1, 1, 2, 3, 3, 3},
561 }, 649 },
562 Service::Mii::RandomMiiData3{ 650 RandomMiiData3{
563 .arg_1 = 0, 651 .arg_1 = 0,
564 .arg_2 = 2, 652 .arg_2 = 2,
565 .values_count = 10, 653 .values_count = 10,
566 .values = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2}, 654 .values = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2},
567 }, 655 },
568 Service::Mii::RandomMiiData3{ 656 RandomMiiData3{
569 .arg_1 = 1, 657 .arg_1 = 1,
570 .arg_2 = 0, 658 .arg_2 = 0,
571 .values_count = 10, 659 .values_count = 10,
572 .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5}, 660 .values = {2, 2, 4, 4, 4, 4, 5, 5, 5, 5},
573 }, 661 },
574 Service::Mii::RandomMiiData3{ 662 RandomMiiData3{
575 .arg_1 = 1, 663 .arg_1 = 1,
576 .arg_2 = 1, 664 .arg_2 = 1,
577 .values_count = 10, 665 .values_count = 10,
578 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 3}, 666 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 3},
579 }, 667 },
580 Service::Mii::RandomMiiData3{ 668 RandomMiiData3{
581 .arg_1 = 1, 669 .arg_1 = 1,
582 .arg_2 = 2, 670 .arg_2 = 2,
583 .values_count = 10, 671 .values_count = 10,
@@ -585,407 +673,407 @@ const std::array<Service::Mii::RandomMiiData3, 6> RandomMiiFacelineColor{
585 }, 673 },
586}; 674};
587 675
588const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineWrinkle{ 676const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle{
589 Service::Mii::RandomMiiData4{ 677 RandomMiiData4{
590 .gender = Gender::Male, 678 .gender = static_cast<u32>(Gender::Male),
591 .age = Age::Young, 679 .age = static_cast<u32>(Age::Young),
592 .race = Race::Black, 680 .race = static_cast<u32>(Race::Black),
593 .values_count = 20, 681 .values_count = 20,
594 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, 682 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
595 }, 683 },
596 Service::Mii::RandomMiiData4{ 684 RandomMiiData4{
597 .gender = Gender::Male, 685 .gender = static_cast<u32>(Gender::Male),
598 .age = Age::Normal, 686 .age = static_cast<u32>(Age::Normal),
599 .race = Race::Black, 687 .race = static_cast<u32>(Race::Black),
600 .values_count = 20, 688 .values_count = 20,
601 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, 689 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
602 }, 690 },
603 Service::Mii::RandomMiiData4{ 691 RandomMiiData4{
604 .gender = Gender::Male, 692 .gender = static_cast<u32>(Gender::Male),
605 .age = Age::Old, 693 .age = static_cast<u32>(Age::Old),
606 .race = Race::Black, 694 .race = static_cast<u32>(Race::Black),
607 .values_count = 20, 695 .values_count = 20,
608 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8}, 696 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8},
609 }, 697 },
610 Service::Mii::RandomMiiData4{ 698 RandomMiiData4{
611 .gender = Gender::Male, 699 .gender = static_cast<u32>(Gender::Male),
612 .age = Age::Young, 700 .age = static_cast<u32>(Age::Young),
613 .race = Race::White, 701 .race = static_cast<u32>(Race::White),
614 .values_count = 20, 702 .values_count = 20,
615 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, 703 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9},
616 }, 704 },
617 Service::Mii::RandomMiiData4{ 705 RandomMiiData4{
618 .gender = Gender::Male, 706 .gender = static_cast<u32>(Gender::Male),
619 .age = Age::Normal, 707 .age = static_cast<u32>(Age::Normal),
620 .race = Race::White, 708 .race = static_cast<u32>(Race::White),
621 .values_count = 20, 709 .values_count = 20,
622 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9}, 710 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9},
623 }, 711 },
624 Service::Mii::RandomMiiData4{ 712 RandomMiiData4{
625 .gender = Gender::Male, 713 .gender = static_cast<u32>(Gender::Male),
626 .age = Age::Old, 714 .age = static_cast<u32>(Age::Old),
627 .race = Race::White, 715 .race = static_cast<u32>(Race::White),
628 .values_count = 20, 716 .values_count = 20,
629 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 717 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
630 }, 718 },
631 Service::Mii::RandomMiiData4{ 719 RandomMiiData4{
632 .gender = Gender::Male, 720 .gender = static_cast<u32>(Gender::Male),
633 .age = Age::Young, 721 .age = static_cast<u32>(Age::Young),
634 .race = Race::Asian, 722 .race = static_cast<u32>(Race::Asian),
635 .values_count = 20, 723 .values_count = 20,
636 .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, 724 .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11},
637 }, 725 },
638 Service::Mii::RandomMiiData4{ 726 RandomMiiData4{
639 .gender = Gender::Male, 727 .gender = static_cast<u32>(Gender::Male),
640 .age = Age::Normal, 728 .age = static_cast<u32>(Age::Normal),
641 .race = Race::Asian, 729 .race = static_cast<u32>(Race::Asian),
642 .values_count = 20, 730 .values_count = 20,
643 .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, 731 .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11},
644 }, 732 },
645 Service::Mii::RandomMiiData4{ 733 RandomMiiData4{
646 .gender = Gender::Male, 734 .gender = static_cast<u32>(Gender::Male),
647 .age = Age::Old, 735 .age = static_cast<u32>(Age::Old),
648 .race = Race::Asian, 736 .race = static_cast<u32>(Race::Asian),
649 .values_count = 20, 737 .values_count = 20,
650 .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11}, 738 .values = {9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11},
651 }, 739 },
652 Service::Mii::RandomMiiData4{ 740 RandomMiiData4{
653 .gender = Gender::Female, 741 .gender = static_cast<u32>(Gender::Female),
654 .age = Age::Young, 742 .age = static_cast<u32>(Age::Young),
655 .race = Race::Black, 743 .race = static_cast<u32>(Race::Black),
656 .values_count = 20, 744 .values_count = 20,
657 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, 745 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
658 }, 746 },
659 Service::Mii::RandomMiiData4{ 747 RandomMiiData4{
660 .gender = Gender::Female, 748 .gender = static_cast<u32>(Gender::Female),
661 .age = Age::Normal, 749 .age = static_cast<u32>(Age::Normal),
662 .race = Race::Black, 750 .race = static_cast<u32>(Race::Black),
663 .values_count = 20, 751 .values_count = 20,
664 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, 752 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
665 }, 753 },
666 Service::Mii::RandomMiiData4{ 754 RandomMiiData4{
667 .gender = Gender::Female, 755 .gender = static_cast<u32>(Gender::Female),
668 .age = Age::Old, 756 .age = static_cast<u32>(Age::Old),
669 .race = Race::Black, 757 .race = static_cast<u32>(Race::Black),
670 .values_count = 20, 758 .values_count = 20,
671 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8}, 759 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8},
672 }, 760 },
673 Service::Mii::RandomMiiData4{ 761 RandomMiiData4{
674 .gender = Gender::Female, 762 .gender = static_cast<u32>(Gender::Female),
675 .age = Age::Young, 763 .age = static_cast<u32>(Age::Young),
676 .race = Race::White, 764 .race = static_cast<u32>(Race::White),
677 .values_count = 20, 765 .values_count = 20,
678 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, 766 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8},
679 }, 767 },
680 Service::Mii::RandomMiiData4{ 768 RandomMiiData4{
681 .gender = Gender::Female, 769 .gender = static_cast<u32>(Gender::Female),
682 .age = Age::Normal, 770 .age = static_cast<u32>(Age::Normal),
683 .race = Race::White, 771 .race = static_cast<u32>(Race::White),
684 .values_count = 20, 772 .values_count = 20,
685 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8}, 773 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4, 8, 8},
686 }, 774 },
687 Service::Mii::RandomMiiData4{ 775 RandomMiiData4{
688 .gender = Gender::Female, 776 .gender = static_cast<u32>(Gender::Female),
689 .age = Age::Old, 777 .age = static_cast<u32>(Age::Old),
690 .race = Race::White, 778 .race = static_cast<u32>(Race::White),
691 .values_count = 20, 779 .values_count = 20,
692 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4}, 780 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 4},
693 }, 781 },
694 Service::Mii::RandomMiiData4{ 782 RandomMiiData4{
695 .gender = Gender::Female, 783 .gender = static_cast<u32>(Gender::Female),
696 .age = Age::Young, 784 .age = static_cast<u32>(Age::Young),
697 .race = Race::Asian, 785 .race = static_cast<u32>(Race::Asian),
698 .values_count = 20, 786 .values_count = 20,
699 .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, 787 .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11},
700 }, 788 },
701 Service::Mii::RandomMiiData4{ 789 RandomMiiData4{
702 .gender = Gender::Female, 790 .gender = static_cast<u32>(Gender::Female),
703 .age = Age::Normal, 791 .age = static_cast<u32>(Age::Normal),
704 .race = Race::Asian, 792 .race = static_cast<u32>(Race::Asian),
705 .values_count = 20, 793 .values_count = 20,
706 .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, 794 .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11},
707 }, 795 },
708 Service::Mii::RandomMiiData4{ 796 RandomMiiData4{
709 .gender = Gender::Female, 797 .gender = static_cast<u32>(Gender::Female),
710 .age = Age::Old, 798 .age = static_cast<u32>(Age::Old),
711 .race = Race::Asian, 799 .race = static_cast<u32>(Race::Asian),
712 .values_count = 20, 800 .values_count = 20,
713 .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11}, 801 .values = {9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11},
714 }, 802 },
715}; 803};
716 804
717const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiFacelineMakeup{ 805const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup{
718 Service::Mii::RandomMiiData4{ 806 RandomMiiData4{
719 .gender = Gender::Male, 807 .gender = static_cast<u32>(Gender::Male),
720 .age = Age::Young, 808 .age = static_cast<u32>(Age::Young),
721 .race = Race::Black, 809 .race = static_cast<u32>(Race::Black),
722 .values_count = 20, 810 .values_count = 20,
723 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 811 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
724 }, 812 },
725 Service::Mii::RandomMiiData4{ 813 RandomMiiData4{
726 .gender = Gender::Male, 814 .gender = static_cast<u32>(Gender::Male),
727 .age = Age::Normal, 815 .age = static_cast<u32>(Age::Normal),
728 .race = Race::Black, 816 .race = static_cast<u32>(Race::Black),
729 .values_count = 20, 817 .values_count = 20,
730 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9}, 818 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9},
731 }, 819 },
732 Service::Mii::RandomMiiData4{ 820 RandomMiiData4{
733 .gender = Gender::Male, 821 .gender = static_cast<u32>(Gender::Male),
734 .age = Age::Old, 822 .age = static_cast<u32>(Age::Old),
735 .race = Race::Black, 823 .race = static_cast<u32>(Race::Black),
736 .values_count = 20, 824 .values_count = 20,
737 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9}, 825 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9},
738 }, 826 },
739 Service::Mii::RandomMiiData4{ 827 RandomMiiData4{
740 .gender = Gender::Male, 828 .gender = static_cast<u32>(Gender::Male),
741 .age = Age::Young, 829 .age = static_cast<u32>(Age::Young),
742 .race = Race::White, 830 .race = static_cast<u32>(Race::White),
743 .values_count = 20, 831 .values_count = 20,
744 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, 832 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
745 }, 833 },
746 Service::Mii::RandomMiiData4{ 834 RandomMiiData4{
747 .gender = Gender::Male, 835 .gender = static_cast<u32>(Gender::Male),
748 .age = Age::Normal, 836 .age = static_cast<u32>(Age::Normal),
749 .race = Race::White, 837 .race = static_cast<u32>(Race::White),
750 .values_count = 20, 838 .values_count = 20,
751 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, 839 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
752 }, 840 },
753 Service::Mii::RandomMiiData4{ 841 RandomMiiData4{
754 .gender = Gender::Male, 842 .gender = static_cast<u32>(Gender::Male),
755 .age = Age::Old, 843 .age = static_cast<u32>(Age::Old),
756 .race = Race::White, 844 .race = static_cast<u32>(Race::White),
757 .values_count = 20, 845 .values_count = 20,
758 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, 846 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
759 }, 847 },
760 Service::Mii::RandomMiiData4{ 848 RandomMiiData4{
761 .gender = Gender::Male, 849 .gender = static_cast<u32>(Gender::Male),
762 .age = Age::Young, 850 .age = static_cast<u32>(Age::Young),
763 .race = Race::Asian, 851 .race = static_cast<u32>(Race::Asian),
764 .values_count = 20, 852 .values_count = 20,
765 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, 853 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
766 }, 854 },
767 Service::Mii::RandomMiiData4{ 855 RandomMiiData4{
768 .gender = Gender::Male, 856 .gender = static_cast<u32>(Gender::Male),
769 .age = Age::Normal, 857 .age = static_cast<u32>(Age::Normal),
770 .race = Race::Asian, 858 .race = static_cast<u32>(Race::Asian),
771 .values_count = 20, 859 .values_count = 20,
772 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, 860 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
773 }, 861 },
774 Service::Mii::RandomMiiData4{ 862 RandomMiiData4{
775 .gender = Gender::Male, 863 .gender = static_cast<u32>(Gender::Male),
776 .age = Age::Old, 864 .age = static_cast<u32>(Age::Old),
777 .race = Race::Asian, 865 .race = static_cast<u32>(Race::Asian),
778 .values_count = 20, 866 .values_count = 20,
779 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9}, 867 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9},
780 }, 868 },
781 Service::Mii::RandomMiiData4{ 869 RandomMiiData4{
782 .gender = Gender::Female, 870 .gender = static_cast<u32>(Gender::Female),
783 .age = Age::Young, 871 .age = static_cast<u32>(Age::Young),
784 .race = Race::Black, 872 .race = static_cast<u32>(Race::Black),
785 .values_count = 20, 873 .values_count = 20,
786 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2}, 874 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2},
787 }, 875 },
788 Service::Mii::RandomMiiData4{ 876 RandomMiiData4{
789 .gender = Gender::Female, 877 .gender = static_cast<u32>(Gender::Female),
790 .age = Age::Normal, 878 .age = static_cast<u32>(Age::Normal),
791 .race = Race::Black, 879 .race = static_cast<u32>(Race::Black),
792 .values_count = 20, 880 .values_count = 20,
793 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, 881 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9},
794 }, 882 },
795 Service::Mii::RandomMiiData4{ 883 RandomMiiData4{
796 .gender = Gender::Female, 884 .gender = static_cast<u32>(Gender::Female),
797 .age = Age::Old, 885 .age = static_cast<u32>(Age::Old),
798 .race = Race::Black, 886 .race = static_cast<u32>(Race::Black),
799 .values_count = 20, 887 .values_count = 20,
800 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9}, 888 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 9, 9},
801 }, 889 },
802 Service::Mii::RandomMiiData4{ 890 RandomMiiData4{
803 .gender = Gender::Female, 891 .gender = static_cast<u32>(Gender::Female),
804 .age = Age::Young, 892 .age = static_cast<u32>(Age::Young),
805 .race = Race::White, 893 .race = static_cast<u32>(Race::White),
806 .values_count = 20, 894 .values_count = 20,
807 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9}, 895 .values = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9},
808 }, 896 },
809 Service::Mii::RandomMiiData4{ 897 RandomMiiData4{
810 .gender = Gender::Female, 898 .gender = static_cast<u32>(Gender::Female),
811 .age = Age::Normal, 899 .age = static_cast<u32>(Age::Normal),
812 .race = Race::White, 900 .race = static_cast<u32>(Race::White),
813 .values_count = 20, 901 .values_count = 20,
814 .values = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9}, 902 .values = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9},
815 }, 903 },
816 Service::Mii::RandomMiiData4{ 904 RandomMiiData4{
817 .gender = Gender::Female, 905 .gender = static_cast<u32>(Gender::Female),
818 .age = Age::Old, 906 .age = static_cast<u32>(Age::Old),
819 .race = Race::White, 907 .race = static_cast<u32>(Race::White),
820 .values_count = 20, 908 .values_count = 20,
821 .values = {0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 9}, 909 .values = {0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 9},
822 }, 910 },
823 Service::Mii::RandomMiiData4{ 911 RandomMiiData4{
824 .gender = Gender::Female, 912 .gender = static_cast<u32>(Gender::Female),
825 .age = Age::Young, 913 .age = static_cast<u32>(Age::Young),
826 .race = Race::Asian, 914 .race = static_cast<u32>(Race::Asian),
827 .values_count = 20, 915 .values_count = 20,
828 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 916 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
829 }, 917 },
830 Service::Mii::RandomMiiData4{ 918 RandomMiiData4{
831 .gender = Gender::Female, 919 .gender = static_cast<u32>(Gender::Female),
832 .age = Age::Normal, 920 .age = static_cast<u32>(Age::Normal),
833 .race = Race::Asian, 921 .race = static_cast<u32>(Race::Asian),
834 .values_count = 20, 922 .values_count = 20,
835 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 923 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
836 }, 924 },
837 Service::Mii::RandomMiiData4{ 925 RandomMiiData4{
838 .gender = Gender::Female, 926 .gender = static_cast<u32>(Gender::Female),
839 .age = Age::Old, 927 .age = static_cast<u32>(Age::Old),
840 .race = Race::Asian, 928 .race = static_cast<u32>(Race::Asian),
841 .values_count = 20, 929 .values_count = 20,
842 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 930 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
843 }, 931 },
844}; 932};
845 933
846const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{ 934const std::array<RandomMiiData4, 18> RandomMiiHairType{
847 Service::Mii::RandomMiiData4{ 935 RandomMiiData4{
848 .gender = Gender::Male, 936 .gender = static_cast<u32>(Gender::Male),
849 .age = Age::Young, 937 .age = static_cast<u32>(Age::Young),
850 .race = Race::Black, 938 .race = static_cast<u32>(Race::Black),
851 .values_count = 30, 939 .values_count = 30,
852 .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 940 .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45,
853 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 75, 76, 86, 89}, 941 47, 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 75, 76, 86, 89},
854 }, 942 },
855 Service::Mii::RandomMiiData4{ 943 RandomMiiData4{
856 .gender = Gender::Male, 944 .gender = static_cast<u32>(Gender::Male),
857 .age = Age::Normal, 945 .age = static_cast<u32>(Age::Normal),
858 .race = Race::Black, 946 .race = static_cast<u32>(Race::Black),
859 .values_count = 31, 947 .values_count = 31,
860 .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 948 .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47,
861 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, 949 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87},
862 }, 950 },
863 Service::Mii::RandomMiiData4{ 951 RandomMiiData4{
864 .gender = Gender::Male, 952 .gender = static_cast<u32>(Gender::Male),
865 .age = Age::Old, 953 .age = static_cast<u32>(Age::Old),
866 .race = Race::Black, 954 .race = static_cast<u32>(Race::Black),
867 .values_count = 31, 955 .values_count = 31,
868 .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47, 956 .values = {13, 23, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 43, 44, 45, 47,
869 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87}, 957 48, 49, 50, 51, 52, 54, 56, 57, 64, 66, 73, 75, 81, 86, 87},
870 }, 958 },
871 Service::Mii::RandomMiiData4{ 959 RandomMiiData4{
872 .gender = Gender::Male, 960 .gender = static_cast<u32>(Gender::Male),
873 .age = Age::Young, 961 .age = static_cast<u32>(Age::Young),
874 .race = Race::White, 962 .race = static_cast<u32>(Race::White),
875 .values_count = 38, 963 .values_count = 38,
876 .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 40, 42, 43, 44, 45, 47, 48, 49, 50, 964 .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 40, 42, 43, 44, 45, 47, 48, 49, 50,
877 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 75, 76, 86, 89}, 965 51, 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 75, 76, 86, 89},
878 }, 966 },
879 Service::Mii::RandomMiiData4{ 967 RandomMiiData4{
880 .gender = Gender::Male, 968 .gender = static_cast<u32>(Gender::Male),
881 .age = Age::Normal, 969 .age = static_cast<u32>(Age::Normal),
882 .race = Race::White, 970 .race = static_cast<u32>(Race::White),
883 .values_count = 39, 971 .values_count = 39,
884 .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, 972 .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51,
885 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, 973 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87},
886 }, 974 },
887 Service::Mii::RandomMiiData4{ 975 RandomMiiData4{
888 .gender = Gender::Male, 976 .gender = static_cast<u32>(Gender::Male),
889 .age = Age::Old, 977 .age = static_cast<u32>(Age::Old),
890 .race = Race::White, 978 .race = static_cast<u32>(Race::White),
891 .values_count = 39, 979 .values_count = 39,
892 .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51, 980 .values = {13, 23, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40, 43, 44, 45, 47, 48, 49, 50, 51,
893 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87}, 981 52, 53, 54, 55, 56, 58, 59, 60, 64, 65, 66, 67, 68, 70, 73, 75, 81, 86, 87},
894 }, 982 },
895 Service::Mii::RandomMiiData4{ 983 RandomMiiData4{
896 .gender = Gender::Male, 984 .gender = static_cast<u32>(Gender::Male),
897 .age = Age::Young, 985 .age = static_cast<u32>(Age::Young),
898 .race = Race::Asian, 986 .race = static_cast<u32>(Race::Asian),
899 .values_count = 18, 987 .values_count = 18,
900 .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, 988 .values = {13, 23, 30, 36, 37, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88},
901 }, 989 },
902 Service::Mii::RandomMiiData4{ 990 RandomMiiData4{
903 .gender = Gender::Male, 991 .gender = static_cast<u32>(Gender::Male),
904 .age = Age::Normal, 992 .age = static_cast<u32>(Age::Normal),
905 .race = Race::Asian, 993 .race = static_cast<u32>(Race::Asian),
906 .values_count = 19, 994 .values_count = 19,
907 .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, 995 .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88},
908 }, 996 },
909 Service::Mii::RandomMiiData4{ 997 RandomMiiData4{
910 .gender = Gender::Male, 998 .gender = static_cast<u32>(Gender::Male),
911 .age = Age::Old, 999 .age = static_cast<u32>(Age::Old),
912 .race = Race::Asian, 1000 .race = static_cast<u32>(Race::Asian),
913 .values_count = 19, 1001 .values_count = 19,
914 .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88}, 1002 .values = {13, 23, 30, 36, 37, 39, 41, 45, 47, 51, 53, 54, 55, 58, 59, 65, 67, 86, 88},
915 }, 1003 },
916 Service::Mii::RandomMiiData4{ 1004 RandomMiiData4{
917 .gender = Gender::Female, 1005 .gender = static_cast<u32>(Gender::Female),
918 .age = Age::Young, 1006 .age = static_cast<u32>(Age::Young),
919 .race = Race::Black, 1007 .race = static_cast<u32>(Race::Black),
920 .values_count = 39, 1008 .values_count = 39,
921 .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1009 .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
922 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 76, 77, 79, 80, 83, 85}, 1010 21, 22, 24, 25, 26, 28, 46, 50, 61, 62, 63, 64, 69, 76, 77, 79, 80, 83, 85},
923 }, 1011 },
924 Service::Mii::RandomMiiData4{ 1012 RandomMiiData4{
925 .gender = Gender::Female, 1013 .gender = static_cast<u32>(Gender::Female),
926 .age = Age::Normal, 1014 .age = static_cast<u32>(Age::Normal),
927 .race = Race::Black, 1015 .race = static_cast<u32>(Race::Black),
928 .values_count = 42, 1016 .values_count = 42,
929 .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 1017 .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14,
930 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 1018 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50,
931 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, 1019 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87},
932 }, 1020 },
933 Service::Mii::RandomMiiData4{ 1021 RandomMiiData4{
934 .gender = Gender::Female, 1022 .gender = static_cast<u32>(Gender::Female),
935 .age = Age::Old, 1023 .age = static_cast<u32>(Age::Old),
936 .race = Race::Black, 1024 .race = static_cast<u32>(Race::Black),
937 .values_count = 42, 1025 .values_count = 42,
938 .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 1026 .values = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14,
939 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50, 1027 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 28, 46, 50,
940 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87}, 1028 61, 62, 63, 64, 69, 72, 74, 77, 78, 82, 83, 84, 85, 87},
941 }, 1029 },
942 Service::Mii::RandomMiiData4{ 1030 RandomMiiData4{
943 .gender = Gender::Female, 1031 .gender = static_cast<u32>(Gender::Female),
944 .age = Age::Young, 1032 .age = static_cast<u32>(Age::Young),
945 .race = Race::White, 1033 .race = static_cast<u32>(Race::White),
946 .values_count = 44, 1034 .values_count = 44,
947 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1035 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
948 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 42, 50, 1036 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 42, 50,
949 58, 60, 62, 63, 64, 69, 71, 76, 79, 80, 81, 82, 83, 86}, 1037 58, 60, 62, 63, 64, 69, 71, 76, 79, 80, 81, 82, 83, 86},
950 }, 1038 },
951 Service::Mii::RandomMiiData4{ 1039 RandomMiiData4{
952 .gender = Gender::Female, 1040 .gender = static_cast<u32>(Gender::Female),
953 .age = Age::Normal, 1041 .age = static_cast<u32>(Age::Normal),
954 .race = Race::White, 1042 .race = static_cast<u32>(Race::White),
955 .values_count = 44, 1043 .values_count = 44,
956 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1044 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
957 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, 1045 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58,
958 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, 1046 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85},
959 }, 1047 },
960 Service::Mii::RandomMiiData4{ 1048 RandomMiiData4{
961 .gender = Gender::Female, 1049 .gender = static_cast<u32>(Gender::Female),
962 .age = Age::Old, 1050 .age = static_cast<u32>(Age::Old),
963 .race = Race::White, 1051 .race = static_cast<u32>(Race::White),
964 .values_count = 44, 1052 .values_count = 44,
965 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1053 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
966 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58, 1054 15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 29, 50, 58,
967 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85}, 1055 60, 62, 63, 64, 69, 71, 72, 74, 79, 81, 82, 83, 84, 85},
968 }, 1056 },
969 Service::Mii::RandomMiiData4{ 1057 RandomMiiData4{
970 .gender = Gender::Female, 1058 .gender = static_cast<u32>(Gender::Female),
971 .age = Age::Young, 1059 .age = static_cast<u32>(Age::Young),
972 .race = Race::Asian, 1060 .race = static_cast<u32>(Race::Asian),
973 .values_count = 24, 1061 .values_count = 24,
974 .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 1062 .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14,
975 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 76, 83}, 1063 16, 17, 18, 20, 21, 24, 25, 58, 62, 69, 76, 83},
976 }, 1064 },
977 Service::Mii::RandomMiiData4{ 1065 RandomMiiData4{
978 .gender = Gender::Female, 1066 .gender = static_cast<u32>(Gender::Female),
979 .age = Age::Normal, 1067 .age = static_cast<u32>(Age::Normal),
980 .race = Race::Asian, 1068 .race = static_cast<u32>(Race::Asian),
981 .values_count = 27, 1069 .values_count = 27,
982 .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 1070 .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17,
983 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, 1071 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85},
984 }, 1072 },
985 Service::Mii::RandomMiiData4{ 1073 RandomMiiData4{
986 .gender = Gender::Female, 1074 .gender = static_cast<u32>(Gender::Female),
987 .age = Age::Old, 1075 .age = static_cast<u32>(Age::Old),
988 .race = Race::Asian, 1076 .race = static_cast<u32>(Race::Asian),
989 .values_count = 27, 1077 .values_count = 27,
990 .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17, 1078 .values = {0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 16, 17,
991 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85}, 1079 18, 20, 21, 24, 25, 58, 62, 69, 74, 76, 81, 83, 85},
@@ -993,55 +1081,55 @@ const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiHairType{
993}; 1081};
994 1082
995const std::array<RandomMiiData3, 9> RandomMiiHairColor{ 1083const std::array<RandomMiiData3, 9> RandomMiiHairColor{
996 Service::Mii::RandomMiiData3{ 1084 RandomMiiData3{
997 .arg_1 = 0, 1085 .arg_1 = 0,
998 .arg_2 = 0, 1086 .arg_2 = 0,
999 .values_count = 20, 1087 .values_count = 20,
1000 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1088 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
1001 }, 1089 },
1002 Service::Mii::RandomMiiData3{ 1090 RandomMiiData3{
1003 .arg_1 = 0, 1091 .arg_1 = 0,
1004 .arg_2 = 1, 1092 .arg_2 = 1,
1005 .values_count = 20, 1093 .values_count = 20,
1006 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1094 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
1007 }, 1095 },
1008 Service::Mii::RandomMiiData3{ 1096 RandomMiiData3{
1009 .arg_1 = 0, 1097 .arg_1 = 0,
1010 .arg_2 = 2, 1098 .arg_2 = 2,
1011 .values_count = 20, 1099 .values_count = 20,
1012 .values = {0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, 1100 .values = {0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
1013 }, 1101 },
1014 Service::Mii::RandomMiiData3{ 1102 RandomMiiData3{
1015 .arg_1 = 1, 1103 .arg_1 = 1,
1016 .arg_2 = 0, 1104 .arg_2 = 0,
1017 .values_count = 20, 1105 .values_count = 20,
1018 .values = {2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7}, 1106 .values = {2, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7},
1019 }, 1107 },
1020 Service::Mii::RandomMiiData3{ 1108 RandomMiiData3{
1021 .arg_1 = 1, 1109 .arg_1 = 1,
1022 .arg_2 = 1, 1110 .arg_2 = 1,
1023 .values_count = 20, 1111 .values_count = 20,
1024 .values = {2, 3, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, 1112 .values = {2, 3, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7},
1025 }, 1113 },
1026 Service::Mii::RandomMiiData3{ 1114 RandomMiiData3{
1027 .arg_1 = 1, 1115 .arg_1 = 1,
1028 .arg_2 = 2, 1116 .arg_2 = 2,
1029 .values_count = 20, 1117 .values_count = 20,
1030 .values = {2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7}, 1118 .values = {2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7},
1031 }, 1119 },
1032 Service::Mii::RandomMiiData3{ 1120 RandomMiiData3{
1033 .arg_1 = 2, 1121 .arg_1 = 2,
1034 .arg_2 = 0, 1122 .arg_2 = 0,
1035 .values_count = 20, 1123 .values_count = 20,
1036 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1}, 1124 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
1037 }, 1125 },
1038 Service::Mii::RandomMiiData3{ 1126 RandomMiiData3{
1039 .arg_1 = 2, 1127 .arg_1 = 2,
1040 .arg_2 = 1, 1128 .arg_2 = 1,
1041 .values_count = 20, 1129 .values_count = 20,
1042 .values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3}, 1130 .values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3},
1043 }, 1131 },
1044 Service::Mii::RandomMiiData3{ 1132 RandomMiiData3{
1045 .arg_1 = 2, 1133 .arg_1 = 2,
1046 .arg_2 = 2, 1134 .arg_2 = 2,
1047 .values_count = 20, 1135 .values_count = 20,
@@ -1049,598 +1137,642 @@ const std::array<RandomMiiData3, 9> RandomMiiHairColor{
1049 }, 1137 },
1050}; 1138};
1051 1139
1052const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyeType{ 1140const std::array<RandomMiiData4, 18> RandomMiiEyeType{
1053 Service::Mii::RandomMiiData4{ 1141 RandomMiiData4{
1054 .gender = Gender::Male, 1142 .gender = static_cast<u32>(Gender::Male),
1055 .age = Age::Young, 1143 .age = static_cast<u32>(Age::Young),
1056 .race = Race::Black, 1144 .race = static_cast<u32>(Race::Black),
1057 .values_count = 26, 1145 .values_count = 26,
1058 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, 1146 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27,
1059 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, 1147 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57},
1060 }, 1148 },
1061 Service::Mii::RandomMiiData4{ 1149 RandomMiiData4{
1062 .gender = Gender::Male, 1150 .gender = static_cast<u32>(Gender::Male),
1063 .age = Age::Normal, 1151 .age = static_cast<u32>(Age::Normal),
1064 .race = Race::Black, 1152 .race = static_cast<u32>(Race::Black),
1065 .values_count = 26, 1153 .values_count = 26,
1066 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27, 1154 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 27,
1067 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57}, 1155 29, 32, 34, 36, 38, 39, 41, 43, 47, 49, 51, 53, 57},
1068 }, 1156 },
1069 Service::Mii::RandomMiiData4{ 1157 RandomMiiData4{
1070 .gender = Gender::Male, 1158 .gender = static_cast<u32>(Gender::Male),
1071 .age = Age::Old, 1159 .age = static_cast<u32>(Age::Old),
1072 .race = Race::Black, 1160 .race = static_cast<u32>(Race::Black),
1073 .values_count = 27, 1161 .values_count = 27,
1074 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 26, 27, 1162 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 26, 27,
1075 29, 32, 34, 36, 38, 39, 41, 43, 47, 48, 49, 53, 57}, 1163 29, 32, 34, 36, 38, 39, 41, 43, 47, 48, 49, 53, 57},
1076 }, 1164 },
1077 Service::Mii::RandomMiiData4{ 1165 RandomMiiData4{
1078 .gender = Gender::Male, 1166 .gender = static_cast<u32>(Gender::Male),
1079 .age = Age::Young, 1167 .age = static_cast<u32>(Age::Young),
1080 .race = Race::White, 1168 .race = static_cast<u32>(Race::White),
1081 .values_count = 35, 1169 .values_count = 35,
1082 .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, 1170 .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29,
1083 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, 1171 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57},
1084 }, 1172 },
1085 Service::Mii::RandomMiiData4{ 1173 RandomMiiData4{
1086 .gender = Gender::Male, 1174 .gender = static_cast<u32>(Gender::Male),
1087 .age = Age::Normal, 1175 .age = static_cast<u32>(Age::Normal),
1088 .race = Race::White, 1176 .race = static_cast<u32>(Race::White),
1089 .values_count = 35, 1177 .values_count = 35,
1090 .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29, 1178 .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 17, 18, 21, 22, 27, 29,
1091 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57}, 1179 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 49, 51, 53, 55, 56, 57},
1092 }, 1180 },
1093 Service::Mii::RandomMiiData4{ 1181 RandomMiiData4{
1094 .gender = Gender::Male, 1182 .gender = static_cast<u32>(Gender::Male),
1095 .age = Age::Old, 1183 .age = static_cast<u32>(Age::Old),
1096 .race = Race::White, 1184 .race = static_cast<u32>(Race::White),
1097 .values_count = 35, 1185 .values_count = 35,
1098 .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 21, 22, 26, 27, 29, 1186 .values = {2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 21, 22, 26, 27, 29,
1099 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 48, 49, 50, 53, 56, 57}, 1187 31, 32, 34, 36, 37, 38, 39, 41, 43, 44, 47, 48, 49, 50, 53, 56, 57},
1100 }, 1188 },
1101 Service::Mii::RandomMiiData4{ 1189 RandomMiiData4{
1102 .gender = Gender::Male, 1190 .gender = static_cast<u32>(Gender::Male),
1103 .age = Age::Young, 1191 .age = static_cast<u32>(Age::Young),
1104 .race = Race::Asian, 1192 .race = static_cast<u32>(Race::Asian),
1105 .values_count = 30, 1193 .values_count = 30,
1106 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 1194 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21,
1107 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, 1195 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57},
1108 }, 1196 },
1109 Service::Mii::RandomMiiData4{ 1197 RandomMiiData4{
1110 .gender = Gender::Male, 1198 .gender = static_cast<u32>(Gender::Male),
1111 .age = Age::Normal, 1199 .age = static_cast<u32>(Age::Normal),
1112 .race = Race::Asian, 1200 .race = static_cast<u32>(Race::Asian),
1113 .values_count = 30, 1201 .values_count = 30,
1114 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21, 1202 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 21,
1115 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57}, 1203 22, 31, 32, 34, 36, 37, 39, 41, 44, 49, 51, 53, 55, 56, 57},
1116 }, 1204 },
1117 Service::Mii::RandomMiiData4{ 1205 RandomMiiData4{
1118 .gender = Gender::Male, 1206 .gender = static_cast<u32>(Gender::Male),
1119 .age = Age::Old, 1207 .age = static_cast<u32>(Age::Old),
1120 .race = Race::Asian, 1208 .race = static_cast<u32>(Race::Asian),
1121 .values_count = 30, 1209 .values_count = 30,
1122 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 22, 1210 .values = {2, 3, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 21, 22,
1123 26, 31, 32, 34, 36, 37, 39, 41, 44, 48, 49, 50, 51, 53, 57}, 1211 26, 31, 32, 34, 36, 37, 39, 41, 44, 48, 49, 50, 51, 53, 57},
1124 }, 1212 },
1125 Service::Mii::RandomMiiData4{ 1213 RandomMiiData4{
1126 .gender = Gender::Female, 1214 .gender = static_cast<u32>(Gender::Female),
1127 .age = Age::Young, 1215 .age = static_cast<u32>(Age::Young),
1128 .race = Race::Black, 1216 .race = static_cast<u32>(Race::Black),
1129 .values_count = 39, 1217 .values_count = 39,
1130 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 1218 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27,
1131 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, 1219 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59},
1132 }, 1220 },
1133 Service::Mii::RandomMiiData4{ 1221 RandomMiiData4{
1134 .gender = Gender::Female, 1222 .gender = static_cast<u32>(Gender::Female),
1135 .age = Age::Normal, 1223 .age = static_cast<u32>(Age::Normal),
1136 .race = Race::Black, 1224 .race = static_cast<u32>(Race::Black),
1137 .values_count = 39, 1225 .values_count = 39,
1138 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27, 1226 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 27,
1139 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, 1227 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59},
1140 }, 1228 },
1141 Service::Mii::RandomMiiData4{ 1229 RandomMiiData4{
1142 .gender = Gender::Female, 1230 .gender = static_cast<u32>(Gender::Female),
1143 .age = Age::Old, 1231 .age = static_cast<u32>(Age::Old),
1144 .race = Race::Black, 1232 .race = static_cast<u32>(Race::Black),
1145 .values_count = 40, 1233 .values_count = 40,
1146 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26, 1234 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 25, 26,
1147 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59}, 1235 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 59},
1148 }, 1236 },
1149 Service::Mii::RandomMiiData4{ 1237 RandomMiiData4{
1150 .gender = Gender::Female, 1238 .gender = static_cast<u32>(Gender::Female),
1151 .age = Age::Young, 1239 .age = static_cast<u32>(Age::Young),
1152 .race = Race::White, 1240 .race = static_cast<u32>(Race::White),
1153 .values_count = 46, 1241 .values_count = 46,
1154 .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 1242 .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17,
1155 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, 1243 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37,
1156 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, 1244 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59},
1157 }, 1245 },
1158 Service::Mii::RandomMiiData4{ 1246 RandomMiiData4{
1159 .gender = Gender::Female, 1247 .gender = static_cast<u32>(Gender::Female),
1160 .age = Age::Normal, 1248 .age = static_cast<u32>(Age::Normal),
1161 .race = Race::White, 1249 .race = static_cast<u32>(Race::White),
1162 .values_count = 46, 1250 .values_count = 46,
1163 .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 1251 .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17,
1164 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37, 1252 18, 19, 20, 21, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35, 37,
1165 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, 1253 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59},
1166 }, 1254 },
1167 Service::Mii::RandomMiiData4{ 1255 RandomMiiData4{
1168 .gender = Gender::Female, 1256 .gender = static_cast<u32>(Gender::Female),
1169 .age = Age::Old, 1257 .age = static_cast<u32>(Age::Old),
1170 .race = Race::White, 1258 .race = static_cast<u32>(Race::White),
1171 .values_count = 46, 1259 .values_count = 46,
1172 .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 1260 .values = {0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18,
1173 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 37, 1261 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 37,
1174 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59}, 1262 38, 39, 40, 41, 42, 45, 46, 47, 48, 53, 54, 57, 58, 59},
1175 }, 1263 },
1176 Service::Mii::RandomMiiData4{ 1264 RandomMiiData4{
1177 .gender = Gender::Female, 1265 .gender = static_cast<u32>(Gender::Female),
1178 .age = Age::Young, 1266 .age = static_cast<u32>(Age::Young),
1179 .race = Race::Asian, 1267 .race = static_cast<u32>(Race::Asian),
1180 .values_count = 34, 1268 .values_count = 34,
1181 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 1269 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23,
1182 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, 1270 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47},
1183 }, 1271 },
1184 Service::Mii::RandomMiiData4{ 1272 RandomMiiData4{
1185 .gender = Gender::Female, 1273 .gender = static_cast<u32>(Gender::Female),
1186 .age = Age::Normal, 1274 .age = static_cast<u32>(Age::Normal),
1187 .race = Race::Asian, 1275 .race = static_cast<u32>(Race::Asian),
1188 .values_count = 34, 1276 .values_count = 34,
1189 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 1277 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23,
1190 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, 1278 24, 25, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47},
1191 }, 1279 },
1192 Service::Mii::RandomMiiData4{ 1280 RandomMiiData4{
1193 .gender = Gender::Female, 1281 .gender = static_cast<u32>(Gender::Female),
1194 .age = Age::Old, 1282 .age = static_cast<u32>(Age::Old),
1195 .race = Race::Asian, 1283 .race = static_cast<u32>(Race::Asian),
1196 .values_count = 35, 1284 .values_count = 35,
1197 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24, 1285 .values = {0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 18, 19, 23, 24,
1198 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47}, 1286 25, 26, 27, 28, 29, 32, 33, 34, 35, 38, 39, 40, 41, 42, 45, 46, 47},
1199 }, 1287 },
1200}; 1288};
1201 1289
1202const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiEyeColor{ 1290const std::array<RandomMiiData2, 3> RandomMiiEyeColor{
1203 Service::Mii::RandomMiiData2{ 1291 RandomMiiData2{
1204 .arg_1 = 0, 1292 .arg_1 = 0,
1205 .values_count = 10, 1293 .values_count = 10,
1206 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 1294 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
1207 }, 1295 },
1208 Service::Mii::RandomMiiData2{ 1296 RandomMiiData2{
1209 .arg_1 = 1, 1297 .arg_1 = 1,
1210 .values_count = 10, 1298 .values_count = 10,
1211 .values = {0, 1, 1, 2, 3, 3, 4, 4, 4, 5}, 1299 .values = {0, 1, 1, 2, 3, 3, 4, 4, 4, 5},
1212 }, 1300 },
1213 Service::Mii::RandomMiiData2{ 1301 RandomMiiData2{
1214 .arg_1 = 2, 1302 .arg_1 = 2,
1215 .values_count = 10, 1303 .values_count = 10,
1216 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 1304 .values = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
1217 }, 1305 },
1218}; 1306};
1219 1307
1220const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiEyebrowType{ 1308const std::array<RandomMiiData4, 18> RandomMiiEyebrowType{
1221 Service::Mii::RandomMiiData4{ 1309 RandomMiiData4{
1222 .gender = Gender::Male, 1310 .gender = static_cast<u32>(Gender::Male),
1223 .age = Age::Young, 1311 .age = static_cast<u32>(Age::Young),
1224 .race = Race::Black, 1312 .race = static_cast<u32>(Race::Black),
1225 .values_count = 18, 1313 .values_count = 18,
1226 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, 1314 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20},
1227 }, 1315 },
1228 Service::Mii::RandomMiiData4{ 1316 RandomMiiData4{
1229 .gender = Gender::Male, 1317 .gender = static_cast<u32>(Gender::Male),
1230 .age = Age::Normal, 1318 .age = static_cast<u32>(Age::Normal),
1231 .race = Race::Black, 1319 .race = static_cast<u32>(Race::Black),
1232 .values_count = 18, 1320 .values_count = 18,
1233 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, 1321 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20},
1234 }, 1322 },
1235 Service::Mii::RandomMiiData4{ 1323 RandomMiiData4{
1236 .gender = Gender::Male, 1324 .gender = static_cast<u32>(Gender::Male),
1237 .age = Age::Old, 1325 .age = static_cast<u32>(Age::Old),
1238 .race = Race::Black, 1326 .race = static_cast<u32>(Race::Black),
1239 .values_count = 18, 1327 .values_count = 18,
1240 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20}, 1328 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 20},
1241 }, 1329 },
1242 Service::Mii::RandomMiiData4{ 1330 RandomMiiData4{
1243 .gender = Gender::Male, 1331 .gender = static_cast<u32>(Gender::Male),
1244 .age = Age::Young, 1332 .age = static_cast<u32>(Age::Young),
1245 .race = Race::White, 1333 .race = static_cast<u32>(Race::White),
1246 .values_count = 23, 1334 .values_count = 23,
1247 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1335 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
1248 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, 1336 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
1249 }, 1337 },
1250 Service::Mii::RandomMiiData4{ 1338 RandomMiiData4{
1251 .gender = Gender::Male, 1339 .gender = static_cast<u32>(Gender::Male),
1252 .age = Age::Normal, 1340 .age = static_cast<u32>(Age::Normal),
1253 .race = Race::White, 1341 .race = static_cast<u32>(Race::White),
1254 .values_count = 23, 1342 .values_count = 23,
1255 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1343 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
1256 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, 1344 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
1257 }, 1345 },
1258 Service::Mii::RandomMiiData4{ 1346 RandomMiiData4{
1259 .gender = Gender::Male, 1347 .gender = static_cast<u32>(Gender::Male),
1260 .age = Age::Old, 1348 .age = static_cast<u32>(Age::Old),
1261 .race = Race::White, 1349 .race = static_cast<u32>(Race::White),
1262 .values_count = 23, 1350 .values_count = 23,
1263 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1351 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
1264 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}, 1352 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22},
1265 }, 1353 },
1266 Service::Mii::RandomMiiData4{ 1354 RandomMiiData4{
1267 .gender = Gender::Male, 1355 .gender = static_cast<u32>(Gender::Male),
1268 .age = Age::Young, 1356 .age = static_cast<u32>(Age::Young),
1269 .race = Race::Asian, 1357 .race = static_cast<u32>(Race::Asian),
1270 .values_count = 21, 1358 .values_count = 21,
1271 .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, 1359 .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22},
1272 }, 1360 },
1273 Service::Mii::RandomMiiData4{ 1361 RandomMiiData4{
1274 .gender = Gender::Male, 1362 .gender = static_cast<u32>(Gender::Male),
1275 .age = Age::Normal, 1363 .age = static_cast<u32>(Age::Normal),
1276 .race = Race::Asian, 1364 .race = static_cast<u32>(Race::Asian),
1277 .values_count = 21, 1365 .values_count = 21,
1278 .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, 1366 .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22},
1279 }, 1367 },
1280 Service::Mii::RandomMiiData4{ 1368 RandomMiiData4{
1281 .gender = Gender::Male, 1369 .gender = static_cast<u32>(Gender::Male),
1282 .age = Age::Old, 1370 .age = static_cast<u32>(Age::Old),
1283 .race = Race::Asian, 1371 .race = static_cast<u32>(Race::Asian),
1284 .values_count = 21, 1372 .values_count = 21,
1285 .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22}, 1373 .values = {0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22},
1286 }, 1374 },
1287 Service::Mii::RandomMiiData4{ 1375 RandomMiiData4{
1288 .gender = Gender::Female, 1376 .gender = static_cast<u32>(Gender::Female),
1289 .age = Age::Young, 1377 .age = static_cast<u32>(Age::Young),
1290 .race = Race::Black, 1378 .race = static_cast<u32>(Race::Black),
1291 .values_count = 9, 1379 .values_count = 9,
1292 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, 1380 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13},
1293 }, 1381 },
1294 Service::Mii::RandomMiiData4{ 1382 RandomMiiData4{
1295 .gender = Gender::Female, 1383 .gender = static_cast<u32>(Gender::Female),
1296 .age = Age::Normal, 1384 .age = static_cast<u32>(Age::Normal),
1297 .race = Race::Black, 1385 .race = static_cast<u32>(Race::Black),
1298 .values_count = 9, 1386 .values_count = 9,
1299 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, 1387 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13},
1300 }, 1388 },
1301 Service::Mii::RandomMiiData4{ 1389 RandomMiiData4{
1302 .gender = Gender::Female, 1390 .gender = static_cast<u32>(Gender::Female),
1303 .age = Age::Old, 1391 .age = static_cast<u32>(Age::Old),
1304 .race = Race::Black, 1392 .race = static_cast<u32>(Race::Black),
1305 .values_count = 9, 1393 .values_count = 9,
1306 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13}, 1394 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13},
1307 }, 1395 },
1308 Service::Mii::RandomMiiData4{ 1396 RandomMiiData4{
1309 .gender = Gender::Female, 1397 .gender = static_cast<u32>(Gender::Female),
1310 .age = Age::Young, 1398 .age = static_cast<u32>(Age::Young),
1311 .race = Race::White, 1399 .race = static_cast<u32>(Race::White),
1312 .values_count = 11, 1400 .values_count = 11,
1313 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, 1401 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19},
1314 }, 1402 },
1315 Service::Mii::RandomMiiData4{ 1403 RandomMiiData4{
1316 .gender = Gender::Female, 1404 .gender = static_cast<u32>(Gender::Female),
1317 .age = Age::Normal, 1405 .age = static_cast<u32>(Age::Normal),
1318 .race = Race::White, 1406 .race = static_cast<u32>(Race::White),
1319 .values_count = 11, 1407 .values_count = 11,
1320 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, 1408 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19},
1321 }, 1409 },
1322 Service::Mii::RandomMiiData4{ 1410 RandomMiiData4{
1323 .gender = Gender::Female, 1411 .gender = static_cast<u32>(Gender::Female),
1324 .age = Age::Old, 1412 .age = static_cast<u32>(Age::Old),
1325 .race = Race::White, 1413 .race = static_cast<u32>(Race::White),
1326 .values_count = 11, 1414 .values_count = 11,
1327 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19}, 1415 .values = {0, 1, 3, 7, 8, 9, 10, 11, 13, 15, 19},
1328 }, 1416 },
1329 Service::Mii::RandomMiiData4{ 1417 RandomMiiData4{
1330 .gender = Gender::Female, 1418 .gender = static_cast<u32>(Gender::Female),
1331 .age = Age::Young, 1419 .age = static_cast<u32>(Age::Young),
1332 .race = Race::Asian, 1420 .race = static_cast<u32>(Race::Asian),
1333 .values_count = 9, 1421 .values_count = 9,
1334 .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, 1422 .values = {0, 3, 7, 8, 9, 10, 11, 13, 15},
1335 }, 1423 },
1336 Service::Mii::RandomMiiData4{ 1424 RandomMiiData4{
1337 .gender = Gender::Female, 1425 .gender = static_cast<u32>(Gender::Female),
1338 .age = Age::Normal, 1426 .age = static_cast<u32>(Age::Normal),
1339 .race = Race::Asian, 1427 .race = static_cast<u32>(Race::Asian),
1340 .values_count = 9, 1428 .values_count = 9,
1341 .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, 1429 .values = {0, 3, 7, 8, 9, 10, 11, 13, 15},
1342 }, 1430 },
1343 Service::Mii::RandomMiiData4{ 1431 RandomMiiData4{
1344 .gender = Gender::Female, 1432 .gender = static_cast<u32>(Gender::Female),
1345 .age = Age::Old, 1433 .age = static_cast<u32>(Age::Old),
1346 .race = Race::Asian, 1434 .race = static_cast<u32>(Race::Asian),
1347 .values_count = 9, 1435 .values_count = 9,
1348 .values = {0, 3, 7, 8, 9, 10, 11, 13, 15}, 1436 .values = {0, 3, 7, 8, 9, 10, 11, 13, 15},
1349 }, 1437 },
1350}; 1438};
1351 1439
1352const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiNoseType{ 1440const std::array<RandomMiiData4, 18> RandomMiiNoseType{
1353 Service::Mii::RandomMiiData4{ 1441 RandomMiiData4{
1354 .gender = Gender::Male, 1442 .gender = static_cast<u32>(Gender::Male),
1355 .age = Age::Young, 1443 .age = static_cast<u32>(Age::Young),
1356 .race = Race::Black, 1444 .race = static_cast<u32>(Race::Black),
1357 .values_count = 11, 1445 .values_count = 11,
1358 .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, 1446 .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14},
1359 }, 1447 },
1360 Service::Mii::RandomMiiData4{ 1448 RandomMiiData4{
1361 .gender = Gender::Male, 1449 .gender = static_cast<u32>(Gender::Male),
1362 .age = Age::Normal, 1450 .age = static_cast<u32>(Age::Normal),
1363 .race = Race::Black, 1451 .race = static_cast<u32>(Race::Black),
1364 .values_count = 11, 1452 .values_count = 11,
1365 .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, 1453 .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14},
1366 }, 1454 },
1367 Service::Mii::RandomMiiData4{ 1455 RandomMiiData4{
1368 .gender = Gender::Male, 1456 .gender = static_cast<u32>(Gender::Male),
1369 .age = Age::Old, 1457 .age = static_cast<u32>(Age::Old),
1370 .race = Race::Black, 1458 .race = static_cast<u32>(Race::Black),
1371 .values_count = 11, 1459 .values_count = 11,
1372 .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14}, 1460 .values = {0, 1, 2, 3, 4, 5, 7, 8, 10, 13, 14},
1373 }, 1461 },
1374 Service::Mii::RandomMiiData4{ 1462 RandomMiiData4{
1375 .gender = Gender::Male, 1463 .gender = static_cast<u32>(Gender::Male),
1376 .age = Age::Young, 1464 .age = static_cast<u32>(Age::Young),
1377 .race = Race::White, 1465 .race = static_cast<u32>(Race::White),
1378 .values_count = 18, 1466 .values_count = 18,
1379 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, 1467 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
1380 }, 1468 },
1381 Service::Mii::RandomMiiData4{ 1469 RandomMiiData4{
1382 .gender = Gender::Male, 1470 .gender = static_cast<u32>(Gender::Male),
1383 .age = Age::Normal, 1471 .age = static_cast<u32>(Age::Normal),
1384 .race = Race::White, 1472 .race = static_cast<u32>(Race::White),
1385 .values_count = 18, 1473 .values_count = 18,
1386 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, 1474 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
1387 }, 1475 },
1388 Service::Mii::RandomMiiData4{ 1476 RandomMiiData4{
1389 .gender = Gender::Male, 1477 .gender = static_cast<u32>(Gender::Male),
1390 .age = Age::Old, 1478 .age = static_cast<u32>(Age::Old),
1391 .race = Race::White, 1479 .race = static_cast<u32>(Race::White),
1392 .values_count = 15, 1480 .values_count = 15,
1393 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, 1481 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16},
1394 }, 1482 },
1395 Service::Mii::RandomMiiData4{ 1483 RandomMiiData4{
1396 .gender = Gender::Male, 1484 .gender = static_cast<u32>(Gender::Male),
1397 .age = Age::Young, 1485 .age = static_cast<u32>(Age::Young),
1398 .race = Race::Asian, 1486 .race = static_cast<u32>(Race::Asian),
1399 .values_count = 18, 1487 .values_count = 18,
1400 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, 1488 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
1401 }, 1489 },
1402 Service::Mii::RandomMiiData4{ 1490 RandomMiiData4{
1403 .gender = Gender::Male, 1491 .gender = static_cast<u32>(Gender::Male),
1404 .age = Age::Normal, 1492 .age = static_cast<u32>(Age::Normal),
1405 .race = Race::Asian, 1493 .race = static_cast<u32>(Race::Asian),
1406 .values_count = 18, 1494 .values_count = 18,
1407 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, 1495 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17},
1408 }, 1496 },
1409 Service::Mii::RandomMiiData4{ 1497 RandomMiiData4{
1410 .gender = Gender::Male, 1498 .gender = static_cast<u32>(Gender::Male),
1411 .age = Age::Old, 1499 .age = static_cast<u32>(Age::Old),
1412 .race = Race::Asian, 1500 .race = static_cast<u32>(Race::Asian),
1413 .values_count = 15, 1501 .values_count = 15,
1414 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16}, 1502 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16},
1415 }, 1503 },
1416 Service::Mii::RandomMiiData4{ 1504 RandomMiiData4{
1417 .gender = Gender::Female, 1505 .gender = static_cast<u32>(Gender::Female),
1418 .age = Age::Young, 1506 .age = static_cast<u32>(Age::Young),
1419 .race = Race::Black, 1507 .race = static_cast<u32>(Race::Black),
1420 .values_count = 8, 1508 .values_count = 8,
1421 .values = {0, 1, 3, 4, 8, 10, 13, 14}, 1509 .values = {0, 1, 3, 4, 8, 10, 13, 14},
1422 }, 1510 },
1423 Service::Mii::RandomMiiData4{ 1511 RandomMiiData4{
1424 .gender = Gender::Female, 1512 .gender = static_cast<u32>(Gender::Female),
1425 .age = Age::Normal, 1513 .age = static_cast<u32>(Age::Normal),
1426 .race = Race::Black, 1514 .race = static_cast<u32>(Race::Black),
1427 .values_count = 8, 1515 .values_count = 8,
1428 .values = {0, 1, 3, 4, 8, 10, 13, 14}, 1516 .values = {0, 1, 3, 4, 8, 10, 13, 14},
1429 }, 1517 },
1430 Service::Mii::RandomMiiData4{ 1518 RandomMiiData4{
1431 .gender = Gender::Female, 1519 .gender = static_cast<u32>(Gender::Female),
1432 .age = Age::Old, 1520 .age = static_cast<u32>(Age::Old),
1433 .race = Race::Black, 1521 .race = static_cast<u32>(Race::Black),
1434 .values_count = 8, 1522 .values_count = 8,
1435 .values = {0, 1, 3, 4, 8, 10, 13, 14}, 1523 .values = {0, 1, 3, 4, 8, 10, 13, 14},
1436 }, 1524 },
1437 Service::Mii::RandomMiiData4{ 1525 RandomMiiData4{
1438 .gender = Gender::Female, 1526 .gender = static_cast<u32>(Gender::Female),
1439 .age = Age::Young, 1527 .age = static_cast<u32>(Age::Young),
1440 .race = Race::White, 1528 .race = static_cast<u32>(Race::White),
1441 .values_count = 12, 1529 .values_count = 12,
1442 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, 1530 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15},
1443 }, 1531 },
1444 Service::Mii::RandomMiiData4{ 1532 RandomMiiData4{
1445 .gender = Gender::Female, 1533 .gender = static_cast<u32>(Gender::Female),
1446 .age = Age::Normal, 1534 .age = static_cast<u32>(Age::Normal),
1447 .race = Race::White, 1535 .race = static_cast<u32>(Race::White),
1448 .values_count = 11, 1536 .values_count = 11,
1449 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, 1537 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15},
1450 }, 1538 },
1451 Service::Mii::RandomMiiData4{ 1539 RandomMiiData4{
1452 .gender = Gender::Female, 1540 .gender = static_cast<u32>(Gender::Female),
1453 .age = Age::Old, 1541 .age = static_cast<u32>(Age::Old),
1454 .race = Race::White, 1542 .race = static_cast<u32>(Race::White),
1455 .values_count = 10, 1543 .values_count = 10,
1456 .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, 1544 .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14},
1457 }, 1545 },
1458 Service::Mii::RandomMiiData4{ 1546 RandomMiiData4{
1459 .gender = Gender::Female, 1547 .gender = static_cast<u32>(Gender::Female),
1460 .age = Age::Young, 1548 .age = static_cast<u32>(Age::Young),
1461 .race = Race::Asian, 1549 .race = static_cast<u32>(Race::Asian),
1462 .values_count = 12, 1550 .values_count = 12,
1463 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15}, 1551 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 14, 15},
1464 }, 1552 },
1465 Service::Mii::RandomMiiData4{ 1553 RandomMiiData4{
1466 .gender = Gender::Female, 1554 .gender = static_cast<u32>(Gender::Female),
1467 .age = Age::Normal, 1555 .age = static_cast<u32>(Age::Normal),
1468 .race = Race::Asian, 1556 .race = static_cast<u32>(Race::Asian),
1469 .values_count = 11, 1557 .values_count = 11,
1470 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15}, 1558 .values = {0, 1, 3, 4, 6, 8, 9, 10, 11, 13, 15},
1471 }, 1559 },
1472 Service::Mii::RandomMiiData4{ 1560 RandomMiiData4{
1473 .gender = Gender::Female, 1561 .gender = static_cast<u32>(Gender::Female),
1474 .age = Age::Old, 1562 .age = static_cast<u32>(Age::Old),
1475 .race = Race::Asian, 1563 .race = static_cast<u32>(Race::Asian),
1476 .values_count = 10, 1564 .values_count = 10,
1477 .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14}, 1565 .values = {0, 1, 3, 4, 6, 8, 10, 11, 13, 14},
1478 }, 1566 },
1479}; 1567};
1480 1568
1481const std::array<Service::Mii::RandomMiiData4, 18> RandomMiiMouthType{ 1569const std::array<RandomMiiData4, 18> RandomMiiMouthType{
1482 Service::Mii::RandomMiiData4{ 1570 RandomMiiData4{
1483 .gender = Gender::Male, 1571 .gender = static_cast<u32>(Gender::Male),
1484 .age = Age::Young, 1572 .age = static_cast<u32>(Age::Young),
1485 .race = Race::Black, 1573 .race = static_cast<u32>(Race::Black),
1486 .values_count = 25, 1574 .values_count = 25,
1487 .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18, 1575 .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 17, 18,
1488 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, 1576 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35},
1489 }, 1577 },
1490 Service::Mii::RandomMiiData4{ 1578 RandomMiiData4{
1491 .gender = Gender::Male, 1579 .gender = static_cast<u32>(Gender::Male),
1492 .age = Age::Normal, 1580 .age = static_cast<u32>(Age::Normal),
1493 .race = Race::Black, 1581 .race = static_cast<u32>(Race::Black),
1494 .values_count = 27, 1582 .values_count = 27,
1495 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 1583 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17,
1496 18, 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35}, 1584 18, 19, 21, 22, 23, 25, 26, 28, 30, 32, 33, 34, 35},
1497 }, 1585 },
1498 Service::Mii::RandomMiiData4{ 1586 RandomMiiData4{
1499 .gender = Gender::Male, 1587 .gender = static_cast<u32>(Gender::Male),
1500 .age = Age::Old, 1588 .age = static_cast<u32>(Age::Old),
1501 .race = Race::Black, 1589 .race = static_cast<u32>(Race::Black),
1502 .values_count = 28, 1590 .values_count = 28,
1503 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 1591 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17,
1504 18, 19, 21, 22, 23, 25, 26, 28, 30, 31, 32, 33, 34, 35}, 1592 18, 19, 21, 22, 23, 25, 26, 28, 30, 31, 32, 33, 34, 35},
1505 }, 1593 },
1506 Service::Mii::RandomMiiData4{ 1594 RandomMiiData4{
1507 .gender = Gender::Male, 1595 .gender = static_cast<u32>(Gender::Male),
1508 .age = Age::Young, 1596 .age = static_cast<u32>(Age::Young),
1509 .race = Race::White, 1597 .race = static_cast<u32>(Race::White),
1510 .values_count = 24, 1598 .values_count = 24,
1511 .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, 1599 .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16,
1512 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, 1600 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
1513 }, 1601 },
1514 Service::Mii::RandomMiiData4{ 1602 RandomMiiData4{
1515 .gender = Gender::Male, 1603 .gender = static_cast<u32>(Gender::Male),
1516 .age = Age::Normal, 1604 .age = static_cast<u32>(Age::Normal),
1517 .race = Race::White, 1605 .race = static_cast<u32>(Race::White),
1518 .values_count = 26, 1606 .values_count = 26,
1519 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1607 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1520 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, 1608 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
1521 }, 1609 },
1522 Service::Mii::RandomMiiData4{ 1610 RandomMiiData4{
1523 .gender = Gender::Male, 1611 .gender = static_cast<u32>(Gender::Male),
1524 .age = Age::Old, 1612 .age = static_cast<u32>(Age::Old),
1525 .race = Race::White, 1613 .race = static_cast<u32>(Race::White),
1526 .values_count = 26, 1614 .values_count = 26,
1527 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1615 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1528 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, 1616 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
1529 }, 1617 },
1530 Service::Mii::RandomMiiData4{ 1618 RandomMiiData4{
1531 .gender = Gender::Male, 1619 .gender = static_cast<u32>(Gender::Male),
1532 .age = Age::Young, 1620 .age = static_cast<u32>(Age::Young),
1533 .race = Race::Asian, 1621 .race = static_cast<u32>(Race::Asian),
1534 .values_count = 24, 1622 .values_count = 24,
1535 .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16, 1623 .values = {0, 2, 3, 6, 7, 8, 9, 10, 12, 14, 15, 16,
1536 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, 1624 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
1537 }, 1625 },
1538 Service::Mii::RandomMiiData4{ 1626 RandomMiiData4{
1539 .gender = Gender::Male, 1627 .gender = static_cast<u32>(Gender::Male),
1540 .age = Age::Normal, 1628 .age = static_cast<u32>(Age::Normal),
1541 .race = Race::Asian, 1629 .race = static_cast<u32>(Race::Asian),
1542 .values_count = 26, 1630 .values_count = 26,
1543 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1631 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1544 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, 1632 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
1545 }, 1633 },
1546 Service::Mii::RandomMiiData4{ 1634 RandomMiiData4{
1547 .gender = Gender::Male, 1635 .gender = static_cast<u32>(Gender::Male),
1548 .age = Age::Old, 1636 .age = static_cast<u32>(Age::Old),
1549 .race = Race::Asian, 1637 .race = static_cast<u32>(Race::Asian),
1550 .values_count = 26, 1638 .values_count = 26,
1551 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1639 .values = {0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1552 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35}, 1640 16, 17, 18, 19, 20, 21, 22, 23, 30, 31, 33, 34, 35},
1553 }, 1641 },
1554 Service::Mii::RandomMiiData4{ 1642 RandomMiiData4{
1555 .gender = Gender::Female, 1643 .gender = static_cast<u32>(Gender::Female),
1556 .age = Age::Young, 1644 .age = static_cast<u32>(Age::Young),
1557 .race = Race::Black, 1645 .race = static_cast<u32>(Race::Black),
1558 .values_count = 25, 1646 .values_count = 25,
1559 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 1647 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15,
1560 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, 1648 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35},
1561 }, 1649 },
1562 Service::Mii::RandomMiiData4{ 1650 RandomMiiData4{
1563 .gender = Gender::Female, 1651 .gender = static_cast<u32>(Gender::Female),
1564 .age = Age::Normal, 1652 .age = static_cast<u32>(Age::Normal),
1565 .race = Race::Black, 1653 .race = static_cast<u32>(Race::Black),
1566 .values_count = 26, 1654 .values_count = 26,
1567 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 1655 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
1568 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, 1656 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35},
1569 }, 1657 },
1570 Service::Mii::RandomMiiData4{ 1658 RandomMiiData4{
1571 .gender = Gender::Female, 1659 .gender = static_cast<u32>(Gender::Female),
1572 .age = Age::Old, 1660 .age = static_cast<u32>(Age::Old),
1573 .race = Race::Black, 1661 .race = static_cast<u32>(Race::Black),
1574 .values_count = 26, 1662 .values_count = 26,
1575 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 1663 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
1576 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35}, 1664 15, 17, 18, 19, 21, 22, 23, 25, 26, 30, 33, 34, 35},
1577 }, 1665 },
1578 Service::Mii::RandomMiiData4{ 1666 RandomMiiData4{
1579 .gender = Gender::Female, 1667 .gender = static_cast<u32>(Gender::Female),
1580 .age = Age::Young, 1668 .age = static_cast<u32>(Age::Young),
1581 .race = Race::White, 1669 .race = static_cast<u32>(Race::White),
1582 .values_count = 25, 1670 .values_count = 25,
1583 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 1671 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15,
1584 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, 1672 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35},
1585 }, 1673 },
1586 Service::Mii::RandomMiiData4{ 1674 RandomMiiData4{
1587 .gender = Gender::Female, 1675 .gender = static_cast<u32>(Gender::Female),
1588 .age = Age::Normal, 1676 .age = static_cast<u32>(Age::Normal),
1589 .race = Race::White, 1677 .race = static_cast<u32>(Race::White),
1590 .values_count = 26, 1678 .values_count = 26,
1591 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 1679 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
1592 15, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35}, 1680 15, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 33, 35},
1593 }, 1681 },
1594 Service::Mii::RandomMiiData4{ 1682 RandomMiiData4{
1595 .gender = Gender::Female, 1683 .gender = static_cast<u32>(Gender::Female),
1596 .age = Age::Old, 1684 .age = static_cast<u32>(Age::Old),
1597 .race = Race::White, 1685 .race = static_cast<u32>(Race::White),
1598 .values_count = 25, 1686 .values_count = 25,
1599 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 1687 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
1600 15, 17, 18, 19, 21, 22, 23, 24, 25, 29, 33, 35}, 1688 15, 17, 18, 19, 21, 22, 23, 24, 25, 29, 33, 35},
1601 }, 1689 },
1602 Service::Mii::RandomMiiData4{ 1690 RandomMiiData4{
1603 .gender = Gender::Female, 1691 .gender = static_cast<u32>(Gender::Female),
1604 .age = Age::Young, 1692 .age = static_cast<u32>(Age::Young),
1605 .race = Race::Asian, 1693 .race = static_cast<u32>(Race::Asian),
1606 .values_count = 24, 1694 .values_count = 24,
1607 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 1695 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14,
1608 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, 1696 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33},
1609 }, 1697 },
1610 Service::Mii::RandomMiiData4{ 1698 RandomMiiData4{
1611 .gender = Gender::Female, 1699 .gender = static_cast<u32>(Gender::Female),
1612 .age = Age::Normal, 1700 .age = static_cast<u32>(Age::Normal),
1613 .race = Race::Asian, 1701 .race = static_cast<u32>(Race::Asian),
1614 .values_count = 25, 1702 .values_count = 25,
1615 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 1703 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
1616 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, 1704 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33},
1617 }, 1705 },
1618 Service::Mii::RandomMiiData4{ 1706 RandomMiiData4{
1619 .gender = Gender::Female, 1707 .gender = static_cast<u32>(Gender::Female),
1620 .age = Age::Old, 1708 .age = static_cast<u32>(Age::Old),
1621 .race = Race::Asian, 1709 .race = static_cast<u32>(Race::Asian),
1622 .values_count = 25, 1710 .values_count = 25,
1623 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 1711 .values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14,
1624 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33}, 1712 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 29, 33},
1625 }, 1713 },
1626}; 1714};
1627 1715
1628const std::array<Service::Mii::RandomMiiData2, 3> RandomMiiGlassType{ 1716const std::array<RandomMiiData2, 3> RandomMiiGlassType{
1629 Service::Mii::RandomMiiData2{ 1717 RandomMiiData2{
1630 .arg_1 = 0, 1718 .arg_1 = 0,
1631 .values_count = 9, 1719 .values_count = 9,
1632 .values = {90, 94, 96, 100, 0, 0, 0, 0, 0}, 1720 .values = {90, 94, 96, 100, 0, 0, 0, 0, 0},
1633 }, 1721 },
1634 Service::Mii::RandomMiiData2{ 1722 RandomMiiData2{
1635 .arg_1 = 1, 1723 .arg_1 = 1,
1636 .values_count = 9, 1724 .values_count = 9,
1637 .values = {83, 86, 90, 93, 94, 96, 98, 100, 0}, 1725 .values = {83, 86, 90, 93, 94, 96, 98, 100, 0},
1638 }, 1726 },
1639 Service::Mii::RandomMiiData2{ 1727 RandomMiiData2{
1640 .arg_1 = 2, 1728 .arg_1 = 2,
1641 .values_count = 9, 1729 .values_count = 9,
1642 .values = {78, 83, 0, 93, 0, 0, 98, 100, 0}, 1730 .values = {78, 83, 0, 93, 0, 0, 98, 100, 0},
1643 }, 1731 },
1644}; 1732};
1645 1733
1734u8 FromVer3GetFacelineColor(u8 color) {
1735 return FromVer3FacelineColorTable[color];
1736}
1737
1738u8 FromVer3GetHairColor(u8 color) {
1739 return FromVer3HairColorTable[color];
1740}
1741
1742u8 FromVer3GetEyeColor(u8 color) {
1743 return FromVer3EyeColorTable[color];
1744}
1745
1746u8 FromVer3GetMouthlineColor(u8 color) {
1747 return FromVer3MouthlineColorTable[color];
1748}
1749
1750u8 FromVer3GetGlassColor(u8 color) {
1751 return FromVer3GlassColorTable[color];
1752}
1753
1754u8 FromVer3GetGlassType(u8 type) {
1755 return FromVer3GlassTypeTable[type];
1756}
1757
1758FacelineColor GetFacelineColorFromVer3(u32 color) {
1759 return static_cast<FacelineColor>(Ver3FacelineColorTable[color]);
1760}
1761
1762CommonColor GetHairColorFromVer3(u32 color) {
1763 return static_cast<CommonColor>(Ver3HairColorTable[color]);
1764}
1765
1766CommonColor GetEyeColorFromVer3(u32 color) {
1767 return static_cast<CommonColor>(Ver3EyeColorTable[color]);
1768}
1769
1770CommonColor GetMouthColorFromVer3(u32 color) {
1771 return static_cast<CommonColor>(Ver3MouthColorTable[color]);
1772}
1773
1774CommonColor GetGlassColorFromVer3(u32 color) {
1775 return static_cast<CommonColor>(Ver3GlassColorTable[color]);
1776}
1777
1646} // namespace Service::Mii::RawData 1778} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/types/raw_data.h b/src/core/hle/service/mii/types/raw_data.h
new file mode 100644
index 000000000..9a4cfa738
--- /dev/null
+++ b/src/core/hle/service/mii/types/raw_data.h
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "core/hle/service/mii/mii_types.h"
9
10namespace Service::Mii::RawData {
11
12struct RandomMiiValues {
13 std::array<u8, 188> values{};
14};
15static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
16
17struct RandomMiiData4 {
18 u32 gender{};
19 u32 age{};
20 u32 race{};
21 u32 values_count{};
22 std::array<u32, 47> values{};
23};
24static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
25
26struct RandomMiiData3 {
27 u32 arg_1;
28 u32 arg_2;
29 u32 values_count;
30 std::array<u32, 47> values{};
31};
32static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
33
34struct RandomMiiData2 {
35 u32 arg_1;
36 u32 values_count;
37 std::array<u32, 47> values{};
38};
39static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
40
41extern const std::array<Service::Mii::DefaultMii, 2> BaseMii;
42extern const std::array<Service::Mii::DefaultMii, 6> DefaultMii;
43
44extern const std::array<u8, 62> EyeRotateLookup;
45extern const std::array<u8, 24> EyebrowRotateLookup;
46
47extern const std::array<RandomMiiData4, 18> RandomMiiFaceline;
48extern const std::array<RandomMiiData3, 6> RandomMiiFacelineColor;
49extern const std::array<RandomMiiData4, 18> RandomMiiFacelineWrinkle;
50extern const std::array<RandomMiiData4, 18> RandomMiiFacelineMakeup;
51extern const std::array<RandomMiiData4, 18> RandomMiiHairType;
52extern const std::array<RandomMiiData3, 9> RandomMiiHairColor;
53extern const std::array<RandomMiiData4, 18> RandomMiiEyeType;
54extern const std::array<RandomMiiData2, 3> RandomMiiEyeColor;
55extern const std::array<RandomMiiData4, 18> RandomMiiEyebrowType;
56extern const std::array<RandomMiiData4, 18> RandomMiiNoseType;
57extern const std::array<RandomMiiData4, 18> RandomMiiMouthType;
58extern const std::array<RandomMiiData2, 3> RandomMiiGlassType;
59
60u8 FromVer3GetFacelineColor(u8 color);
61u8 FromVer3GetHairColor(u8 color);
62u8 FromVer3GetEyeColor(u8 color);
63u8 FromVer3GetMouthlineColor(u8 color);
64u8 FromVer3GetGlassColor(u8 color);
65u8 FromVer3GetGlassType(u8 type);
66
67FacelineColor GetFacelineColorFromVer3(u32 color);
68CommonColor GetHairColorFromVer3(u32 color);
69CommonColor GetEyeColorFromVer3(u32 color);
70CommonColor GetMouthColorFromVer3(u32 color);
71CommonColor GetGlassColorFromVer3(u32 color);
72
73} // namespace Service::Mii::RawData
diff --git a/src/core/hle/service/mii/types/store_data.cpp b/src/core/hle/service/mii/types/store_data.cpp
new file mode 100644
index 000000000..8fce636c7
--- /dev/null
+++ b/src/core/hle/service/mii/types/store_data.cpp
@@ -0,0 +1,643 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/mii/mii_util.h"
5#include "core/hle/service/mii/types/raw_data.h"
6#include "core/hle/service/mii/types/store_data.h"
7
8namespace Service::Mii {
9
10void StoreData::BuildDefault(u32 mii_index) {
11 const auto& default_mii = RawData::DefaultMii[mii_index];
12 core_data.SetDefault();
13
14 core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type));
15 core_data.SetFacelineColor(
16 RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color)));
17 core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle));
18 core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup));
19
20 core_data.SetHairType(static_cast<HairType>(default_mii.hair_type));
21 core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color)));
22 core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip));
23 core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type));
24 core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color)));
25 core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale));
26 core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect));
27 core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate));
28 core_data.SetEyeX(static_cast<u8>(default_mii.eye_x));
29 core_data.SetEyeY(static_cast<u8>(default_mii.eye_y));
30
31 core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type));
32 core_data.SetEyebrowColor(
33 RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color)));
34 core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale));
35 core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
36 core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
37 core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
38 core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y));
39
40 core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
41 core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
42 core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
43
44 core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type));
45 core_data.SetMouthColor(
46 RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
47 core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
48 core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect));
49 core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y));
50
51 core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type));
52 core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type));
53 core_data.SetBeardColor(
54 RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color)));
55 core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale));
56 core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y));
57
58 core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type));
59 core_data.SetGlassColor(
60 RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color)));
61 core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale));
62 core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y));
63
64 core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type));
65 core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale));
66 core_data.SetMoleX(static_cast<u8>(default_mii.mole_x));
67 core_data.SetMoleY(static_cast<u8>(default_mii.mole_y));
68
69 core_data.SetHeight(static_cast<u8>(default_mii.height));
70 core_data.SetBuild(static_cast<u8>(default_mii.weight));
71 core_data.SetGender(static_cast<Gender>(default_mii.gender));
72 core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color));
73 core_data.SetRegionMove(static_cast<u8>(default_mii.region_move));
74 core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region));
75 core_data.SetType(static_cast<u8>(default_mii.type));
76 core_data.SetNickname(default_mii.nickname);
77
78 const auto device_id = MiiUtil::GetDeviceId();
79 create_id = MiiUtil::MakeCreateId();
80 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
81 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
82}
83
84void StoreData::BuildBase(Gender gender) {
85 const auto& default_mii = RawData::BaseMii[gender == Gender::Female ? 1 : 0];
86 core_data.SetDefault();
87
88 core_data.SetFacelineType(static_cast<FacelineType>(default_mii.face_type));
89 core_data.SetFacelineColor(
90 RawData::GetFacelineColorFromVer3(static_cast<u8>(default_mii.face_color)));
91 core_data.SetFacelineWrinkle(static_cast<FacelineWrinkle>(default_mii.face_wrinkle));
92 core_data.SetFacelineMake(static_cast<FacelineMake>(default_mii.face_makeup));
93
94 core_data.SetHairType(static_cast<HairType>(default_mii.hair_type));
95 core_data.SetHairColor(RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.hair_color)));
96 core_data.SetHairFlip(static_cast<HairFlip>(default_mii.hair_flip));
97 core_data.SetEyeType(static_cast<EyeType>(default_mii.eye_type));
98 core_data.SetEyeColor(RawData::GetEyeColorFromVer3(static_cast<u8>(default_mii.eye_color)));
99 core_data.SetEyeScale(static_cast<u8>(default_mii.eye_scale));
100 core_data.SetEyeAspect(static_cast<u8>(default_mii.eye_aspect));
101 core_data.SetEyeRotate(static_cast<u8>(default_mii.eye_rotate));
102 core_data.SetEyeX(static_cast<u8>(default_mii.eye_x));
103 core_data.SetEyeY(static_cast<u8>(default_mii.eye_y));
104
105 core_data.SetEyebrowType(static_cast<EyebrowType>(default_mii.eyebrow_type));
106 core_data.SetEyebrowColor(
107 RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.eyebrow_color)));
108 core_data.SetEyebrowScale(static_cast<u8>(default_mii.eyebrow_scale));
109 core_data.SetEyebrowAspect(static_cast<u8>(default_mii.eyebrow_aspect));
110 core_data.SetEyebrowRotate(static_cast<u8>(default_mii.eyebrow_rotate));
111 core_data.SetEyebrowX(static_cast<u8>(default_mii.eyebrow_x));
112 core_data.SetEyebrowY(static_cast<u8>(default_mii.eyebrow_y));
113
114 core_data.SetNoseType(static_cast<NoseType>(default_mii.nose_type));
115 core_data.SetNoseScale(static_cast<u8>(default_mii.nose_scale));
116 core_data.SetNoseY(static_cast<u8>(default_mii.nose_y));
117
118 core_data.SetMouthType(static_cast<u8>(default_mii.mouth_type));
119 core_data.SetMouthColor(
120 RawData::GetMouthColorFromVer3(static_cast<u8>(default_mii.mouth_color)));
121 core_data.SetMouthScale(static_cast<u8>(default_mii.mouth_scale));
122 core_data.SetMouthAspect(static_cast<u8>(default_mii.mouth_aspect));
123 core_data.SetMouthY(static_cast<u8>(default_mii.mouth_y));
124
125 core_data.SetMustacheType(static_cast<MustacheType>(default_mii.mustache_type));
126 core_data.SetBeardType(static_cast<BeardType>(default_mii.beard_type));
127 core_data.SetBeardColor(
128 RawData::GetHairColorFromVer3(static_cast<u8>(default_mii.beard_color)));
129 core_data.SetMustacheScale(static_cast<u8>(default_mii.mustache_scale));
130 core_data.SetMustacheY(static_cast<u8>(default_mii.mustache_y));
131
132 core_data.SetGlassType(static_cast<GlassType>(default_mii.glasses_type));
133 core_data.SetGlassColor(
134 RawData::GetGlassColorFromVer3(static_cast<u8>(default_mii.glasses_color)));
135 core_data.SetGlassScale(static_cast<u8>(default_mii.glasses_scale));
136 core_data.SetGlassY(static_cast<u8>(default_mii.glasses_y));
137
138 core_data.SetMoleType(static_cast<MoleType>(default_mii.mole_type));
139 core_data.SetMoleScale(static_cast<u8>(default_mii.mole_scale));
140 core_data.SetMoleX(static_cast<u8>(default_mii.mole_x));
141 core_data.SetMoleY(static_cast<u8>(default_mii.mole_y));
142
143 core_data.SetHeight(static_cast<u8>(default_mii.height));
144 core_data.SetBuild(static_cast<u8>(default_mii.weight));
145 core_data.SetGender(static_cast<Gender>(default_mii.gender));
146 core_data.SetFavoriteColor(static_cast<FavoriteColor>(default_mii.favorite_color));
147 core_data.SetRegionMove(static_cast<u8>(default_mii.region_move));
148 core_data.SetFontRegion(static_cast<FontRegion>(default_mii.font_region));
149 core_data.SetType(static_cast<u8>(default_mii.type));
150 core_data.SetNickname(default_mii.nickname);
151
152 const auto device_id = MiiUtil::GetDeviceId();
153 create_id = MiiUtil::MakeCreateId();
154 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
155 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
156}
157
158void StoreData::BuildRandom(Age age, Gender gender, Race race) {
159 core_data.BuildRandom(age, gender, race);
160 const auto device_id = MiiUtil::GetDeviceId();
161 create_id = MiiUtil::MakeCreateId();
162 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
163 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
164}
165
166void StoreData::SetInvalidName() {
167 const auto& invalid_name = core_data.GetInvalidNickname();
168 const auto device_id = MiiUtil::GetDeviceId();
169 core_data.SetNickname(invalid_name);
170 device_crc = MiiUtil::CalculateCrc16(&device_id, sizeof(Common::UUID));
171 data_crc = MiiUtil::CalculateCrc16(&core_data, sizeof(CoreData));
172}
173
174bool StoreData::IsSpecial() const {
175 return GetType() == 1;
176}
177
178u32 StoreData::IsValid() const {
179 // TODO: complete this
180 return 0;
181}
182
183void StoreData::SetFontRegion(FontRegion value) {
184 core_data.SetFontRegion(value);
185}
186
187void StoreData::SetFavoriteColor(FavoriteColor value) {
188 core_data.SetFavoriteColor(value);
189}
190
191void StoreData::SetGender(Gender value) {
192 core_data.SetGender(value);
193}
194
195void StoreData::SetHeight(u8 value) {
196 core_data.SetHeight(value);
197}
198
199void StoreData::SetBuild(u8 value) {
200 core_data.SetBuild(value);
201}
202
203void StoreData::SetType(u8 value) {
204 core_data.SetType(value);
205}
206
207void StoreData::SetRegionMove(u8 value) {
208 core_data.SetRegionMove(value);
209}
210
211void StoreData::SetFacelineType(FacelineType value) {
212 core_data.SetFacelineType(value);
213}
214
215void StoreData::SetFacelineColor(FacelineColor value) {
216 core_data.SetFacelineColor(value);
217}
218
219void StoreData::SetFacelineWrinkle(FacelineWrinkle value) {
220 core_data.SetFacelineWrinkle(value);
221}
222
223void StoreData::SetFacelineMake(FacelineMake value) {
224 core_data.SetFacelineMake(value);
225}
226
227void StoreData::SetHairType(HairType value) {
228 core_data.SetHairType(value);
229}
230
231void StoreData::SetHairColor(CommonColor value) {
232 core_data.SetHairColor(value);
233}
234
235void StoreData::SetHairFlip(HairFlip value) {
236 core_data.SetHairFlip(value);
237}
238
239void StoreData::SetEyeType(EyeType value) {
240 core_data.SetEyeType(value);
241}
242
243void StoreData::SetEyeColor(CommonColor value) {
244 core_data.SetEyeColor(value);
245}
246
247void StoreData::SetEyeScale(u8 value) {
248 core_data.SetEyeScale(value);
249}
250
251void StoreData::SetEyeAspect(u8 value) {
252 core_data.SetEyeAspect(value);
253}
254
255void StoreData::SetEyeRotate(u8 value) {
256 core_data.SetEyeRotate(value);
257}
258
259void StoreData::SetEyeX(u8 value) {
260 core_data.SetEyeX(value);
261}
262
263void StoreData::SetEyeY(u8 value) {
264 core_data.SetEyeY(value);
265}
266
267void StoreData::SetEyebrowType(EyebrowType value) {
268 core_data.SetEyebrowType(value);
269}
270
271void StoreData::SetEyebrowColor(CommonColor value) {
272 core_data.SetEyebrowColor(value);
273}
274
275void StoreData::SetEyebrowScale(u8 value) {
276 core_data.SetEyebrowScale(value);
277}
278
279void StoreData::SetEyebrowAspect(u8 value) {
280 core_data.SetEyebrowAspect(value);
281}
282
283void StoreData::SetEyebrowRotate(u8 value) {
284 core_data.SetEyebrowRotate(value);
285}
286
287void StoreData::SetEyebrowX(u8 value) {
288 core_data.SetEyebrowX(value);
289}
290
291void StoreData::SetEyebrowY(u8 value) {
292 core_data.SetEyebrowY(value);
293}
294
295void StoreData::SetNoseType(NoseType value) {
296 core_data.SetNoseType(value);
297}
298
299void StoreData::SetNoseScale(u8 value) {
300 core_data.SetNoseScale(value);
301}
302
303void StoreData::SetNoseY(u8 value) {
304 core_data.SetNoseY(value);
305}
306
307void StoreData::SetMouthType(u8 value) {
308 core_data.SetMouthType(value);
309}
310
311void StoreData::SetMouthColor(CommonColor value) {
312 core_data.SetMouthColor(value);
313}
314
315void StoreData::SetMouthScale(u8 value) {
316 core_data.SetMouthScale(value);
317}
318
319void StoreData::SetMouthAspect(u8 value) {
320 core_data.SetMouthAspect(value);
321}
322
323void StoreData::SetMouthY(u8 value) {
324 core_data.SetMouthY(value);
325}
326
327void StoreData::SetBeardColor(CommonColor value) {
328 core_data.SetBeardColor(value);
329}
330
331void StoreData::SetBeardType(BeardType value) {
332 core_data.SetBeardType(value);
333}
334
335void StoreData::SetMustacheType(MustacheType value) {
336 core_data.SetMustacheType(value);
337}
338
339void StoreData::SetMustacheScale(u8 value) {
340 core_data.SetMustacheScale(value);
341}
342
343void StoreData::SetMustacheY(u8 value) {
344 core_data.SetMustacheY(value);
345}
346
347void StoreData::SetGlassType(GlassType value) {
348 core_data.SetGlassType(value);
349}
350
351void StoreData::SetGlassColor(CommonColor value) {
352 core_data.SetGlassColor(value);
353}
354
355void StoreData::SetGlassScale(u8 value) {
356 core_data.SetGlassScale(value);
357}
358
359void StoreData::SetGlassY(u8 value) {
360 core_data.SetGlassY(value);
361}
362
363void StoreData::SetMoleType(MoleType value) {
364 core_data.SetMoleType(value);
365}
366
367void StoreData::SetMoleScale(u8 value) {
368 core_data.SetMoleScale(value);
369}
370
371void StoreData::SetMoleX(u8 value) {
372 core_data.SetMoleX(value);
373}
374
375void StoreData::SetMoleY(u8 value) {
376 core_data.SetMoleY(value);
377}
378
379void StoreData::SetNickname(Nickname value) {
380 core_data.SetNickname(value);
381}
382
383Common::UUID StoreData::GetCreateId() const {
384 return create_id;
385}
386
387FontRegion StoreData::GetFontRegion() const {
388 return static_cast<FontRegion>(core_data.GetFontRegion());
389}
390
391FavoriteColor StoreData::GetFavoriteColor() const {
392 return core_data.GetFavoriteColor();
393}
394
395Gender StoreData::GetGender() const {
396 return core_data.GetGender();
397}
398
399u8 StoreData::GetHeight() const {
400 return core_data.GetHeight();
401}
402
403u8 StoreData::GetBuild() const {
404 return core_data.GetBuild();
405}
406
407u8 StoreData::GetType() const {
408 return core_data.GetType();
409}
410
411u8 StoreData::GetRegionMove() const {
412 return core_data.GetRegionMove();
413}
414
415FacelineType StoreData::GetFacelineType() const {
416 return core_data.GetFacelineType();
417}
418
419FacelineColor StoreData::GetFacelineColor() const {
420 return core_data.GetFacelineColor();
421}
422
423FacelineWrinkle StoreData::GetFacelineWrinkle() const {
424 return core_data.GetFacelineWrinkle();
425}
426
427FacelineMake StoreData::GetFacelineMake() const {
428 return core_data.GetFacelineMake();
429}
430
431HairType StoreData::GetHairType() const {
432 return core_data.GetHairType();
433}
434
435CommonColor StoreData::GetHairColor() const {
436 return core_data.GetHairColor();
437}
438
439HairFlip StoreData::GetHairFlip() const {
440 return core_data.GetHairFlip();
441}
442
443EyeType StoreData::GetEyeType() const {
444 return core_data.GetEyeType();
445}
446
447CommonColor StoreData::GetEyeColor() const {
448 return core_data.GetEyeColor();
449}
450
451u8 StoreData::GetEyeScale() const {
452 return core_data.GetEyeScale();
453}
454
455u8 StoreData::GetEyeAspect() const {
456 return core_data.GetEyeAspect();
457}
458
459u8 StoreData::GetEyeRotate() const {
460 return core_data.GetEyeRotate();
461}
462
463u8 StoreData::GetEyeX() const {
464 return core_data.GetEyeX();
465}
466
467u8 StoreData::GetEyeY() const {
468 return core_data.GetEyeY();
469}
470
471EyebrowType StoreData::GetEyebrowType() const {
472 return core_data.GetEyebrowType();
473}
474
475CommonColor StoreData::GetEyebrowColor() const {
476 return core_data.GetEyebrowColor();
477}
478
479u8 StoreData::GetEyebrowScale() const {
480 return core_data.GetEyebrowScale();
481}
482
483u8 StoreData::GetEyebrowAspect() const {
484 return core_data.GetEyebrowAspect();
485}
486
487u8 StoreData::GetEyebrowRotate() const {
488 return core_data.GetEyebrowRotate();
489}
490
491u8 StoreData::GetEyebrowX() const {
492 return core_data.GetEyebrowX();
493}
494
495u8 StoreData::GetEyebrowY() const {
496 return core_data.GetEyebrowY();
497}
498
499NoseType StoreData::GetNoseType() const {
500 return core_data.GetNoseType();
501}
502
503u8 StoreData::GetNoseScale() const {
504 return core_data.GetNoseScale();
505}
506
507u8 StoreData::GetNoseY() const {
508 return core_data.GetNoseY();
509}
510
511MouthType StoreData::GetMouthType() const {
512 return core_data.GetMouthType();
513}
514
515CommonColor StoreData::GetMouthColor() const {
516 return core_data.GetMouthColor();
517}
518
519u8 StoreData::GetMouthScale() const {
520 return core_data.GetMouthScale();
521}
522
523u8 StoreData::GetMouthAspect() const {
524 return core_data.GetMouthAspect();
525}
526
527u8 StoreData::GetMouthY() const {
528 return core_data.GetMouthY();
529}
530
531CommonColor StoreData::GetBeardColor() const {
532 return core_data.GetBeardColor();
533}
534
535BeardType StoreData::GetBeardType() const {
536 return core_data.GetBeardType();
537}
538
539MustacheType StoreData::GetMustacheType() const {
540 return core_data.GetMustacheType();
541}
542
543u8 StoreData::GetMustacheScale() const {
544 return core_data.GetMustacheScale();
545}
546
547u8 StoreData::GetMustacheY() const {
548 return core_data.GetMustacheY();
549}
550
551GlassType StoreData::GetGlassType() const {
552 return core_data.GetGlassType();
553}
554
555CommonColor StoreData::GetGlassColor() const {
556 return core_data.GetGlassColor();
557}
558
559u8 StoreData::GetGlassScale() const {
560 return core_data.GetGlassScale();
561}
562
563u8 StoreData::GetGlassY() const {
564 return core_data.GetGlassY();
565}
566
567MoleType StoreData::GetMoleType() const {
568 return core_data.GetMoleType();
569}
570
571u8 StoreData::GetMoleScale() const {
572 return core_data.GetMoleScale();
573}
574
575u8 StoreData::GetMoleX() const {
576 return core_data.GetMoleX();
577}
578
579u8 StoreData::GetMoleY() const {
580 return core_data.GetMoleY();
581}
582
583Nickname StoreData::GetNickname() const {
584 return core_data.GetNickname();
585}
586
587bool StoreData::operator==(const StoreData& data) {
588 bool is_identical = data.core_data.IsValid() == 0;
589 is_identical &= core_data.GetNickname().data == data.core_data.GetNickname().data;
590 is_identical &= GetCreateId() == data.GetCreateId();
591 is_identical &= GetFontRegion() == data.GetFontRegion();
592 is_identical &= GetFavoriteColor() == data.GetFavoriteColor();
593 is_identical &= GetGender() == data.GetGender();
594 is_identical &= GetHeight() == data.GetHeight();
595 is_identical &= GetBuild() == data.GetBuild();
596 is_identical &= GetType() == data.GetType();
597 is_identical &= GetRegionMove() == data.GetRegionMove();
598 is_identical &= GetFacelineType() == data.GetFacelineType();
599 is_identical &= GetFacelineColor() == data.GetFacelineColor();
600 is_identical &= GetFacelineWrinkle() == data.GetFacelineWrinkle();
601 is_identical &= GetFacelineMake() == data.GetFacelineMake();
602 is_identical &= GetHairType() == data.GetHairType();
603 is_identical &= GetHairColor() == data.GetHairColor();
604 is_identical &= GetHairFlip() == data.GetHairFlip();
605 is_identical &= GetEyeType() == data.GetEyeType();
606 is_identical &= GetEyeColor() == data.GetEyeColor();
607 is_identical &= GetEyeScale() == data.GetEyeScale();
608 is_identical &= GetEyeAspect() == data.GetEyeAspect();
609 is_identical &= GetEyeRotate() == data.GetEyeRotate();
610 is_identical &= GetEyeX() == data.GetEyeX();
611 is_identical &= GetEyeY() == data.GetEyeY();
612 is_identical &= GetEyebrowType() == data.GetEyebrowType();
613 is_identical &= GetEyebrowColor() == data.GetEyebrowColor();
614 is_identical &= GetEyebrowScale() == data.GetEyebrowScale();
615 is_identical &= GetEyebrowAspect() == data.GetEyebrowAspect();
616 is_identical &= GetEyebrowRotate() == data.GetEyebrowRotate();
617 is_identical &= GetEyebrowX() == data.GetEyebrowX();
618 is_identical &= GetEyebrowY() == data.GetEyebrowY();
619 is_identical &= GetNoseType() == data.GetNoseType();
620 is_identical &= GetNoseScale() == data.GetNoseScale();
621 is_identical &= GetNoseY() == data.GetNoseY();
622 is_identical &= GetMouthType() == data.GetMouthType();
623 is_identical &= GetMouthColor() == data.GetMouthColor();
624 is_identical &= GetMouthScale() == data.GetMouthScale();
625 is_identical &= GetMouthAspect() == data.GetMouthAspect();
626 is_identical &= GetMouthY() == data.GetMouthY();
627 is_identical &= GetBeardColor() == data.GetBeardColor();
628 is_identical &= GetBeardType() == data.GetBeardType();
629 is_identical &= GetMustacheType() == data.GetMustacheType();
630 is_identical &= GetMustacheScale() == data.GetMustacheScale();
631 is_identical &= GetMustacheY() == data.GetMustacheY();
632 is_identical &= GetGlassType() == data.GetGlassType();
633 is_identical &= GetGlassColor() == data.GetGlassColor();
634 is_identical &= GetGlassScale() == data.GetGlassScale();
635 is_identical &= GetGlassY() == data.GetGlassY();
636 is_identical &= GetMoleType() == data.GetMoleType();
637 is_identical &= GetMoleScale() == data.GetMoleScale();
638 is_identical &= GetMoleX() == data.GetMoleX();
639 is_identical &= data.GetMoleY() == data.GetMoleY();
640 return is_identical;
641}
642
643} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/store_data.h b/src/core/hle/service/mii/types/store_data.h
new file mode 100644
index 000000000..224c32cf8
--- /dev/null
+++ b/src/core/hle/service/mii/types/store_data.h
@@ -0,0 +1,145 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/mii/mii_types.h"
7#include "core/hle/service/mii/types/core_data.h"
8
9namespace Service::Mii {
10
11class StoreData {
12public:
13 // nn::mii::detail::StoreDataRaw::BuildDefault
14 void BuildDefault(u32 mii_index);
15 // nn::mii::detail::StoreDataRaw::BuildDefault
16
17 void BuildBase(Gender gender);
18 // nn::mii::detail::StoreDataRaw::BuildRandom
19 void BuildRandom(Age age, Gender gender, Race race);
20
21 bool IsSpecial() const;
22
23 u32 IsValid() const;
24
25 void SetFontRegion(FontRegion value);
26 void SetFavoriteColor(FavoriteColor value);
27 void SetGender(Gender value);
28 void SetHeight(u8 value);
29 void SetBuild(u8 value);
30 void SetType(u8 value);
31 void SetRegionMove(u8 value);
32 void SetFacelineType(FacelineType value);
33 void SetFacelineColor(FacelineColor value);
34 void SetFacelineWrinkle(FacelineWrinkle value);
35 void SetFacelineMake(FacelineMake value);
36 void SetHairType(HairType value);
37 void SetHairColor(CommonColor value);
38 void SetHairFlip(HairFlip value);
39 void SetEyeType(EyeType value);
40 void SetEyeColor(CommonColor value);
41 void SetEyeScale(u8 value);
42 void SetEyeAspect(u8 value);
43 void SetEyeRotate(u8 value);
44 void SetEyeX(u8 value);
45 void SetEyeY(u8 value);
46 void SetEyebrowType(EyebrowType value);
47 void SetEyebrowColor(CommonColor value);
48 void SetEyebrowScale(u8 value);
49 void SetEyebrowAspect(u8 value);
50 void SetEyebrowRotate(u8 value);
51 void SetEyebrowX(u8 value);
52 void SetEyebrowY(u8 value);
53 void SetNoseType(NoseType value);
54 void SetNoseScale(u8 value);
55 void SetNoseY(u8 value);
56 void SetMouthType(u8 value);
57 void SetMouthColor(CommonColor value);
58 void SetMouthScale(u8 value);
59 void SetMouthAspect(u8 value);
60 void SetMouthY(u8 value);
61 void SetBeardColor(CommonColor value);
62 void SetBeardType(BeardType value);
63 void SetMustacheType(MustacheType value);
64 void SetMustacheScale(u8 value);
65 void SetMustacheY(u8 value);
66 void SetGlassType(GlassType value);
67 void SetGlassColor(CommonColor value);
68 void SetGlassScale(u8 value);
69 void SetGlassY(u8 value);
70 void SetMoleType(MoleType value);
71 void SetMoleScale(u8 value);
72 void SetMoleX(u8 value);
73 void SetMoleY(u8 value);
74 void SetNickname(Nickname nickname);
75 void SetInvalidName();
76
77 Common::UUID GetCreateId() const;
78 FontRegion GetFontRegion() const;
79 FavoriteColor GetFavoriteColor() const;
80 Gender GetGender() const;
81 u8 GetHeight() const;
82 u8 GetBuild() const;
83 u8 GetType() const;
84 u8 GetRegionMove() const;
85 FacelineType GetFacelineType() const;
86 FacelineColor GetFacelineColor() const;
87 FacelineWrinkle GetFacelineWrinkle() const;
88 FacelineMake GetFacelineMake() const;
89 HairType GetHairType() const;
90 CommonColor GetHairColor() const;
91 HairFlip GetHairFlip() const;
92 EyeType GetEyeType() const;
93 CommonColor GetEyeColor() const;
94 u8 GetEyeScale() const;
95 u8 GetEyeAspect() const;
96 u8 GetEyeRotate() const;
97 u8 GetEyeX() const;
98 u8 GetEyeY() const;
99 EyebrowType GetEyebrowType() const;
100 CommonColor GetEyebrowColor() const;
101 u8 GetEyebrowScale() const;
102 u8 GetEyebrowAspect() const;
103 u8 GetEyebrowRotate() const;
104 u8 GetEyebrowX() const;
105 u8 GetEyebrowY() const;
106 NoseType GetNoseType() const;
107 u8 GetNoseScale() const;
108 u8 GetNoseY() const;
109 MouthType GetMouthType() const;
110 CommonColor GetMouthColor() const;
111 u8 GetMouthScale() const;
112 u8 GetMouthAspect() const;
113 u8 GetMouthY() const;
114 CommonColor GetBeardColor() const;
115 BeardType GetBeardType() const;
116 MustacheType GetMustacheType() const;
117 u8 GetMustacheScale() const;
118 u8 GetMustacheY() const;
119 GlassType GetGlassType() const;
120 CommonColor GetGlassColor() const;
121 u8 GetGlassScale() const;
122 u8 GetGlassY() const;
123 MoleType GetMoleType() const;
124 u8 GetMoleScale() const;
125 u8 GetMoleX() const;
126 u8 GetMoleY() const;
127 Nickname GetNickname() const;
128
129 bool operator==(const StoreData& data);
130
131private:
132 CoreData core_data{};
133 Common::UUID create_id{};
134 u16 data_crc{};
135 u16 device_crc{};
136};
137static_assert(sizeof(StoreData) == 0x44, "StoreData has incorrect size.");
138
139struct StoreDataElement {
140 StoreData store_data{};
141 Source source{};
142};
143static_assert(sizeof(StoreDataElement) == 0x48, "StoreDataElement has incorrect size.");
144
145}; // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/ver3_store_data.cpp b/src/core/hle/service/mii/types/ver3_store_data.cpp
new file mode 100644
index 000000000..1c28e0b1b
--- /dev/null
+++ b/src/core/hle/service/mii/types/ver3_store_data.cpp
@@ -0,0 +1,241 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/mii/mii_util.h"
5#include "core/hle/service/mii/types/raw_data.h"
6#include "core/hle/service/mii/types/store_data.h"
7#include "core/hle/service/mii/types/ver3_store_data.h"
8
9namespace Service::Mii {
10
11void NfpStoreDataExtension::SetFromStoreData(const StoreData& store_data) {
12 faceline_color = static_cast<u8>(store_data.GetFacelineColor()) & 0xf;
13 hair_color = static_cast<u8>(store_data.GetHairColor()) & 0x7f;
14 eye_color = static_cast<u8>(store_data.GetEyeColor()) & 0x7f;
15 eyebrow_color = static_cast<u8>(store_data.GetEyebrowColor()) & 0x7f;
16 mouth_color = static_cast<u8>(store_data.GetMouthColor()) & 0x7f;
17 beard_color = static_cast<u8>(store_data.GetBeardColor()) & 0x7f;
18 glass_color = static_cast<u8>(store_data.GetGlassColor()) & 0x7f;
19 glass_type = static_cast<u8>(store_data.GetGlassType()) & 0x1f;
20}
21
22void Ver3StoreData::BuildToStoreData(StoreData& out_store_data) const {
23 out_store_data.BuildBase(Gender::Male);
24
25 if (!IsValid()) {
26 return;
27 }
28
29 // TODO: We are ignoring a bunch of data from the mii_v3
30
31 out_store_data.SetGender(static_cast<Gender>(mii_information.gender.Value()));
32 out_store_data.SetFavoriteColor(
33 static_cast<FavoriteColor>(mii_information.favorite_color.Value()));
34 out_store_data.SetHeight(height);
35 out_store_data.SetBuild(build);
36
37 out_store_data.SetNickname(mii_name);
38 out_store_data.SetFontRegion(
39 static_cast<FontRegion>(static_cast<u8>(region_information.font_region)));
40
41 out_store_data.SetFacelineType(
42 static_cast<FacelineType>(appearance_bits1.faceline_type.Value()));
43 out_store_data.SetFacelineColor(
44 static_cast<FacelineColor>(appearance_bits1.faceline_color.Value()));
45 out_store_data.SetFacelineWrinkle(
46 static_cast<FacelineWrinkle>(appearance_bits2.faceline_wrinkle.Value()));
47 out_store_data.SetFacelineMake(
48 static_cast<FacelineMake>(appearance_bits2.faceline_make.Value()));
49
50 out_store_data.SetHairType(static_cast<HairType>(hair_type));
51 out_store_data.SetHairColor(static_cast<CommonColor>(appearance_bits3.hair_color.Value()));
52 out_store_data.SetHairFlip(static_cast<HairFlip>(appearance_bits3.hair_flip.Value()));
53
54 out_store_data.SetEyeType(static_cast<EyeType>(appearance_bits4.eye_type.Value()));
55 out_store_data.SetEyeColor(static_cast<CommonColor>(appearance_bits4.eye_color.Value()));
56 out_store_data.SetEyeScale(static_cast<u8>(appearance_bits4.eye_scale));
57 out_store_data.SetEyeAspect(static_cast<u8>(appearance_bits4.eye_aspect));
58 out_store_data.SetEyeRotate(static_cast<u8>(appearance_bits4.eye_rotate));
59 out_store_data.SetEyeX(static_cast<u8>(appearance_bits4.eye_x));
60 out_store_data.SetEyeY(static_cast<u8>(appearance_bits4.eye_y));
61
62 out_store_data.SetEyebrowType(static_cast<EyebrowType>(appearance_bits5.eyebrow_type.Value()));
63 out_store_data.SetEyebrowColor(
64 static_cast<CommonColor>(appearance_bits5.eyebrow_color.Value()));
65 out_store_data.SetEyebrowScale(static_cast<u8>(appearance_bits5.eyebrow_scale));
66 out_store_data.SetEyebrowAspect(static_cast<u8>(appearance_bits5.eyebrow_aspect));
67 out_store_data.SetEyebrowRotate(static_cast<u8>(appearance_bits5.eyebrow_rotate));
68 out_store_data.SetEyebrowX(static_cast<u8>(appearance_bits5.eyebrow_x));
69 out_store_data.SetEyebrowY(static_cast<u8>(appearance_bits5.eyebrow_y));
70
71 out_store_data.SetNoseType(static_cast<NoseType>(appearance_bits6.nose_type.Value()));
72 out_store_data.SetNoseScale(static_cast<u8>(appearance_bits6.nose_scale));
73 out_store_data.SetNoseY(static_cast<u8>(appearance_bits6.nose_y));
74
75 out_store_data.SetMouthType(static_cast<u8>(appearance_bits7.mouth_type));
76 out_store_data.SetMouthColor(static_cast<CommonColor>(appearance_bits7.mouth_color.Value()));
77 out_store_data.SetMouthScale(static_cast<u8>(appearance_bits7.mouth_scale));
78 out_store_data.SetMouthAspect(static_cast<u8>(appearance_bits7.mouth_aspect));
79 out_store_data.SetMouthY(static_cast<u8>(appearance_bits8.mouth_y));
80
81 out_store_data.SetMustacheType(
82 static_cast<MustacheType>(appearance_bits8.mustache_type.Value()));
83 out_store_data.SetMustacheScale(static_cast<u8>(appearance_bits9.mustache_scale));
84 out_store_data.SetMustacheY(static_cast<u8>(appearance_bits9.mustache_y));
85
86 out_store_data.SetBeardType(static_cast<BeardType>(appearance_bits9.beard_type.Value()));
87 out_store_data.SetBeardColor(static_cast<CommonColor>(appearance_bits9.beard_color.Value()));
88
89 out_store_data.SetGlassType(static_cast<GlassType>(appearance_bits10.glass_type.Value()));
90 out_store_data.SetGlassColor(static_cast<CommonColor>(appearance_bits10.glass_color.Value()));
91 out_store_data.SetGlassScale(static_cast<u8>(appearance_bits10.glass_scale));
92 out_store_data.SetGlassY(static_cast<u8>(appearance_bits10.glass_y));
93
94 out_store_data.SetMoleType(static_cast<MoleType>(appearance_bits11.mole_type.Value()));
95 out_store_data.SetMoleScale(static_cast<u8>(appearance_bits11.mole_scale));
96 out_store_data.SetMoleX(static_cast<u8>(appearance_bits11.mole_x));
97 out_store_data.SetMoleY(static_cast<u8>(appearance_bits11.mole_y));
98}
99
100void Ver3StoreData::BuildFromStoreData(const StoreData& store_data) {
101 version = 1;
102 mii_information.gender.Assign(static_cast<u8>(store_data.GetGender()));
103 mii_information.favorite_color.Assign(static_cast<u8>(store_data.GetFavoriteColor()));
104 height = store_data.GetHeight();
105 build = store_data.GetBuild();
106
107 mii_name = store_data.GetNickname();
108 region_information.font_region.Assign(static_cast<u8>(store_data.GetFontRegion()));
109
110 appearance_bits1.faceline_type.Assign(static_cast<u8>(store_data.GetFacelineType()));
111 appearance_bits2.faceline_wrinkle.Assign(static_cast<u8>(store_data.GetFacelineWrinkle()));
112 appearance_bits2.faceline_make.Assign(static_cast<u8>(store_data.GetFacelineMake()));
113
114 hair_type = static_cast<u8>(store_data.GetHairType());
115 appearance_bits3.hair_flip.Assign(static_cast<u8>(store_data.GetHairFlip()));
116
117 appearance_bits4.eye_type.Assign(static_cast<u8>(store_data.GetEyeType()));
118 appearance_bits4.eye_scale.Assign(store_data.GetEyeScale());
119 appearance_bits4.eye_aspect.Assign(store_data.GetEyebrowAspect());
120 appearance_bits4.eye_rotate.Assign(store_data.GetEyeRotate());
121 appearance_bits4.eye_x.Assign(store_data.GetEyeX());
122 appearance_bits4.eye_y.Assign(store_data.GetEyeY());
123
124 appearance_bits5.eyebrow_type.Assign(static_cast<u8>(store_data.GetEyebrowType()));
125 appearance_bits5.eyebrow_scale.Assign(store_data.GetEyebrowScale());
126 appearance_bits5.eyebrow_aspect.Assign(store_data.GetEyebrowAspect());
127 appearance_bits5.eyebrow_rotate.Assign(store_data.GetEyebrowRotate());
128 appearance_bits5.eyebrow_x.Assign(store_data.GetEyebrowX());
129 appearance_bits5.eyebrow_y.Assign(store_data.GetEyebrowY());
130
131 appearance_bits6.nose_type.Assign(static_cast<u8>(store_data.GetNoseType()));
132 appearance_bits6.nose_scale.Assign(store_data.GetNoseScale());
133 appearance_bits6.nose_y.Assign(store_data.GetNoseY());
134
135 appearance_bits7.mouth_type.Assign(static_cast<u8>(store_data.GetMouthType()));
136 appearance_bits7.mouth_scale.Assign(store_data.GetMouthScale());
137 appearance_bits7.mouth_aspect.Assign(store_data.GetMouthAspect());
138 appearance_bits8.mouth_y.Assign(store_data.GetMouthY());
139
140 appearance_bits8.mustache_type.Assign(static_cast<u8>(store_data.GetMustacheType()));
141 appearance_bits9.mustache_scale.Assign(store_data.GetMustacheScale());
142 appearance_bits9.mustache_y.Assign(store_data.GetMustacheY());
143
144 appearance_bits9.beard_type.Assign(static_cast<u8>(store_data.GetBeardType()));
145
146 appearance_bits10.glass_scale.Assign(store_data.GetGlassScale());
147 appearance_bits10.glass_y.Assign(store_data.GetGlassY());
148
149 appearance_bits11.mole_type.Assign(static_cast<u8>(store_data.GetMoleType()));
150 appearance_bits11.mole_scale.Assign(store_data.GetMoleScale());
151 appearance_bits11.mole_x.Assign(store_data.GetMoleX());
152 appearance_bits11.mole_y.Assign(store_data.GetMoleY());
153
154 // These types are converted to V3 from a table
155 appearance_bits1.faceline_color.Assign(
156 RawData::FromVer3GetFacelineColor(static_cast<u8>(store_data.GetFacelineColor())));
157 appearance_bits3.hair_color.Assign(
158 RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetHairColor())));
159 appearance_bits4.eye_color.Assign(
160 RawData::FromVer3GetEyeColor(static_cast<u8>(store_data.GetEyeColor())));
161 appearance_bits5.eyebrow_color.Assign(
162 RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetEyebrowColor())));
163 appearance_bits7.mouth_color.Assign(
164 RawData::FromVer3GetMouthlineColor(static_cast<u8>(store_data.GetMouthColor())));
165 appearance_bits9.beard_color.Assign(
166 RawData::FromVer3GetHairColor(static_cast<u8>(store_data.GetBeardColor())));
167 appearance_bits10.glass_color.Assign(
168 RawData::FromVer3GetGlassColor(static_cast<u8>(store_data.GetGlassColor())));
169 appearance_bits10.glass_type.Assign(
170 RawData::FromVer3GetGlassType(static_cast<u8>(store_data.GetGlassType())));
171
172 crc = MiiUtil::CalculateCrc16(&version, sizeof(Ver3StoreData) - sizeof(u16));
173}
174
175u32 Ver3StoreData::IsValid() const {
176 bool is_valid = version == 0 || version == 3;
177
178 is_valid = is_valid && (mii_name.data[0] != '\0');
179
180 is_valid = is_valid && (mii_information.birth_month < 13);
181 is_valid = is_valid && (mii_information.birth_day < 32);
182 is_valid = is_valid && (mii_information.favorite_color <= static_cast<u8>(FavoriteColor::Max));
183 is_valid = is_valid && (height <= MaxHeight);
184 is_valid = is_valid && (build <= MaxBuild);
185
186 is_valid = is_valid && (appearance_bits1.faceline_type <= static_cast<u8>(FacelineType::Max));
187 is_valid = is_valid && (appearance_bits1.faceline_color <= MaxVer3CommonColor - 2);
188 is_valid =
189 is_valid && (appearance_bits2.faceline_wrinkle <= static_cast<u8>(FacelineWrinkle::Max));
190 is_valid = is_valid && (appearance_bits2.faceline_make <= static_cast<u8>(FacelineMake::Max));
191
192 is_valid = is_valid && (hair_type <= static_cast<u8>(HairType::Max));
193 is_valid = is_valid && (appearance_bits3.hair_color <= MaxVer3CommonColor);
194
195 is_valid = is_valid && (appearance_bits4.eye_type <= static_cast<u8>(EyeType::Max));
196 is_valid = is_valid && (appearance_bits4.eye_color <= MaxVer3CommonColor - 2);
197 is_valid = is_valid && (appearance_bits4.eye_scale <= MaxEyeScale);
198 is_valid = is_valid && (appearance_bits4.eye_aspect <= MaxEyeAspect);
199 is_valid = is_valid && (appearance_bits4.eye_rotate <= MaxEyeRotate);
200 is_valid = is_valid && (appearance_bits4.eye_x <= MaxEyeX);
201 is_valid = is_valid && (appearance_bits4.eye_y <= MaxEyeY);
202
203 is_valid = is_valid && (appearance_bits5.eyebrow_type <= static_cast<u8>(EyebrowType::Max));
204 is_valid = is_valid && (appearance_bits5.eyebrow_color <= MaxVer3CommonColor);
205 is_valid = is_valid && (appearance_bits5.eyebrow_scale <= MaxEyebrowScale);
206 is_valid = is_valid && (appearance_bits5.eyebrow_aspect <= MaxEyebrowAspect);
207 is_valid = is_valid && (appearance_bits5.eyebrow_rotate <= MaxEyebrowRotate);
208 is_valid = is_valid && (appearance_bits5.eyebrow_x <= MaxEyebrowX);
209 is_valid = is_valid && (appearance_bits5.eyebrow_y <= MaxEyebrowY);
210
211 is_valid = is_valid && (appearance_bits6.nose_type <= static_cast<u8>(NoseType::Max));
212 is_valid = is_valid && (appearance_bits6.nose_scale <= MaxNoseScale);
213 is_valid = is_valid && (appearance_bits6.nose_y <= MaxNoseY);
214
215 is_valid = is_valid && (appearance_bits7.mouth_type <= static_cast<u8>(MouthType::Max));
216 is_valid = is_valid && (appearance_bits7.mouth_color <= MaxVer3CommonColor - 3);
217 is_valid = is_valid && (appearance_bits7.mouth_scale <= MaxMouthScale);
218 is_valid = is_valid && (appearance_bits7.mouth_aspect <= MaxMoutAspect);
219 is_valid = is_valid && (appearance_bits8.mouth_y <= MaxMouthY);
220
221 is_valid = is_valid && (appearance_bits8.mustache_type <= static_cast<u8>(MustacheType::Max));
222 is_valid = is_valid && (appearance_bits9.mustache_scale < MaxMustacheScale);
223 is_valid = is_valid && (appearance_bits9.mustache_y <= MasMustacheY);
224
225 is_valid = is_valid && (appearance_bits9.beard_type <= static_cast<u8>(BeardType::Max));
226 is_valid = is_valid && (appearance_bits9.beard_color <= MaxVer3CommonColor);
227
228 is_valid = is_valid && (appearance_bits10.glass_type <= MaxVer3GlassType);
229 is_valid = is_valid && (appearance_bits10.glass_color <= MaxVer3CommonColor - 2);
230 is_valid = is_valid && (appearance_bits10.glass_scale <= MaxGlassScale);
231 is_valid = is_valid && (appearance_bits10.glass_y <= MaxGlassScale);
232
233 is_valid = is_valid && (appearance_bits11.mole_type <= static_cast<u8>(MoleType::Max));
234 is_valid = is_valid && (appearance_bits11.mole_scale <= MaxMoleScale);
235 is_valid = is_valid && (appearance_bits11.mole_x <= MaxMoleX);
236 is_valid = is_valid && (appearance_bits11.mole_y <= MaxMoleY);
237
238 return is_valid;
239}
240
241} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/types/ver3_store_data.h b/src/core/hle/service/mii/types/ver3_store_data.h
new file mode 100644
index 000000000..47907bf7d
--- /dev/null
+++ b/src/core/hle/service/mii/types/ver3_store_data.h
@@ -0,0 +1,160 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/mii/mii_types.h"
7
8namespace Service::Mii {
9class StoreData;
10
11// This is nn::mii::Ver3StoreData
12// Based on citra HLE::Applets::MiiData and PretendoNetwork.
13// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
14// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
15
16struct NfpStoreDataExtension {
17 void SetFromStoreData(const StoreData& store_data);
18
19 u8 faceline_color;
20 u8 hair_color;
21 u8 eye_color;
22 u8 eyebrow_color;
23 u8 mouth_color;
24 u8 beard_color;
25 u8 glass_color;
26 u8 glass_type;
27};
28static_assert(sizeof(NfpStoreDataExtension) == 0x8, "NfpStoreDataExtension is an invalid size");
29
30#pragma pack(push, 4)
31class Ver3StoreData {
32public:
33 void BuildToStoreData(StoreData& out_store_data) const;
34 void BuildFromStoreData(const StoreData& store_data);
35
36 u32 IsValid() const;
37
38 u8 version;
39 union {
40 u8 raw;
41
42 BitField<0, 1, u8> allow_copying;
43 BitField<1, 1, u8> profanity_flag;
44 BitField<2, 2, u8> region_lock;
45 BitField<4, 2, u8> font_region;
46 } region_information;
47 u16_be mii_id;
48 u64_be system_id;
49 u32_be specialness_and_creation_date;
50 std::array<u8, 6> creator_mac;
51 u16_be padding;
52 union {
53 u16 raw;
54
55 BitField<0, 1, u16> gender;
56 BitField<1, 4, u16> birth_month;
57 BitField<5, 5, u16> birth_day;
58 BitField<10, 4, u16> favorite_color;
59 BitField<14, 1, u16> favorite;
60 } mii_information;
61 Nickname mii_name;
62 u8 height;
63 u8 build;
64 union {
65 u8 raw;
66
67 BitField<0, 1, u8> disable_sharing;
68 BitField<1, 4, u8> faceline_type;
69 BitField<5, 3, u8> faceline_color;
70 } appearance_bits1;
71 union {
72 u8 raw;
73
74 BitField<0, 4, u8> faceline_wrinkle;
75 BitField<4, 4, u8> faceline_make;
76 } appearance_bits2;
77 u8 hair_type;
78 union {
79 u8 raw;
80
81 BitField<0, 3, u8> hair_color;
82 BitField<3, 1, u8> hair_flip;
83 } appearance_bits3;
84 union {
85 u32 raw;
86
87 BitField<0, 6, u32> eye_type;
88 BitField<6, 3, u32> eye_color;
89 BitField<9, 4, u32> eye_scale;
90 BitField<13, 3, u32> eye_aspect;
91 BitField<16, 5, u32> eye_rotate;
92 BitField<21, 4, u32> eye_x;
93 BitField<25, 5, u32> eye_y;
94 } appearance_bits4;
95 union {
96 u32 raw;
97
98 BitField<0, 5, u32> eyebrow_type;
99 BitField<5, 3, u32> eyebrow_color;
100 BitField<8, 4, u32> eyebrow_scale;
101 BitField<12, 3, u32> eyebrow_aspect;
102 BitField<16, 4, u32> eyebrow_rotate;
103 BitField<21, 4, u32> eyebrow_x;
104 BitField<25, 5, u32> eyebrow_y;
105 } appearance_bits5;
106 union {
107 u16 raw;
108
109 BitField<0, 5, u16> nose_type;
110 BitField<5, 4, u16> nose_scale;
111 BitField<9, 5, u16> nose_y;
112 } appearance_bits6;
113 union {
114 u16 raw;
115
116 BitField<0, 6, u16> mouth_type;
117 BitField<6, 3, u16> mouth_color;
118 BitField<9, 4, u16> mouth_scale;
119 BitField<13, 3, u16> mouth_aspect;
120 } appearance_bits7;
121 union {
122 u8 raw;
123
124 BitField<0, 5, u8> mouth_y;
125 BitField<5, 3, u8> mustache_type;
126 } appearance_bits8;
127 u8 allow_copying;
128 union {
129 u16 raw;
130
131 BitField<0, 3, u16> beard_type;
132 BitField<3, 3, u16> beard_color;
133 BitField<6, 4, u16> mustache_scale;
134 BitField<10, 5, u16> mustache_y;
135 } appearance_bits9;
136 union {
137 u16 raw;
138
139 BitField<0, 4, u16> glass_type;
140 BitField<4, 3, u16> glass_color;
141 BitField<7, 4, u16> glass_scale;
142 BitField<11, 5, u16> glass_y;
143 } appearance_bits10;
144 union {
145 u16 raw;
146
147 BitField<0, 1, u16> mole_type;
148 BitField<1, 4, u16> mole_scale;
149 BitField<5, 5, u16> mole_x;
150 BitField<10, 5, u16> mole_y;
151 } appearance_bits11;
152
153 Nickname author_name;
154 INSERT_PADDING_BYTES(0x2);
155 u16_be crc;
156};
157static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
158#pragma pack(pop)
159
160}; // namespace Service::Mii
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 49446bc42..5dda12343 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -28,7 +28,6 @@
28#include "core/hle/kernel/k_event.h" 28#include "core/hle/kernel/k_event.h"
29#include "core/hle/service/ipc_helpers.h" 29#include "core/hle/service/ipc_helpers.h"
30#include "core/hle/service/mii/mii_manager.h" 30#include "core/hle/service/mii/mii_manager.h"
31#include "core/hle/service/mii/types.h"
32#include "core/hle/service/nfc/common/amiibo_crypto.h" 31#include "core/hle/service/nfc/common/amiibo_crypto.h"
33#include "core/hle/service/nfc/common/device.h" 32#include "core/hle/service/nfc/common/device.h"
34#include "core/hle/service/nfc/mifare_result.h" 33#include "core/hle/service/nfc/mifare_result.h"
@@ -681,12 +680,16 @@ Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const {
681 return ResultRegistrationIsNotInitialized; 680 return ResultRegistrationIsNotInitialized;
682 } 681 }
683 682
684 Service::Mii::MiiManager manager; 683 Mii::CharInfo char_info{};
684 Mii::StoreData store_data{};
685 tag_data.owner_mii.BuildToStoreData(store_data);
686 char_info.SetFromStoreData(store_data);
687
685 const auto& settings = tag_data.settings; 688 const auto& settings = tag_data.settings;
686 689
687 // TODO: Validate this data 690 // TODO: Validate this data
688 register_info = { 691 register_info = {
689 .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), 692 .mii_char_info = char_info,
690 .creation_date = settings.init_date.GetWriteDate(), 693 .creation_date = settings.init_date.GetWriteDate(),
691 .amiibo_name = GetAmiiboName(settings), 694 .amiibo_name = GetAmiiboName(settings),
692 .font_region = settings.settings.font_region, 695 .font_region = settings.settings.font_region,
@@ -825,8 +828,11 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
825 return ResultWrongDeviceState; 828 return ResultWrongDeviceState;
826 } 829 }
827 830
828 Service::Mii::MiiManager manager; 831 Service::Mii::StoreData store_data{};
829 const auto mii = manager.BuildDefault(0); 832 Service::Mii::NfpStoreDataExtension extension{};
833 store_data.BuildBase(Mii::Gender::Male);
834 extension.SetFromStoreData(store_data);
835
830 auto& settings = tag_data.settings; 836 auto& settings = tag_data.settings;
831 837
832 if (tag_data.settings.settings.amiibo_initialized == 0) { 838 if (tag_data.settings.settings.amiibo_initialized == 0) {
@@ -835,8 +841,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
835 } 841 }
836 842
837 SetAmiiboName(settings, register_info.amiibo_name); 843 SetAmiiboName(settings, register_info.amiibo_name);
838 tag_data.owner_mii = manager.BuildFromStoreData(mii); 844 tag_data.owner_mii.BuildFromStoreData(store_data);
839 tag_data.mii_extension = manager.SetFromStoreData(mii); 845 tag_data.mii_extension = extension;
840 tag_data.unknown = 0; 846 tag_data.unknown = 0;
841 tag_data.unknown2 = {}; 847 tag_data.unknown2 = {};
842 settings.country_code_id = 0; 848 settings.country_code_id = 0;
@@ -1453,7 +1459,7 @@ void NfcDevice::UpdateRegisterInfoCrc() {
1453 1459
1454void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, 1460void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
1455 const NFP::EncryptedNTAG215File& encrypted_file) const { 1461 const NFP::EncryptedNTAG215File& encrypted_file) const {
1456 Service::Mii::MiiManager manager; 1462 Service::Mii::StoreData store_data{};
1457 auto& settings = stubbed_tag_data.settings; 1463 auto& settings = stubbed_tag_data.settings;
1458 1464
1459 stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file); 1465 stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file);
@@ -1467,7 +1473,8 @@ void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
1467 SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'}); 1473 SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'});
1468 settings.settings.font_region.Assign(0); 1474 settings.settings.font_region.Assign(0);
1469 settings.init_date = GetAmiiboDate(GetCurrentPosixTime()); 1475 settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
1470 stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); 1476 store_data.BuildBase(Mii::Gender::Male);
1477 stubbed_tag_data.owner_mii.BuildFromStoreData(store_data);
1471 1478
1472 // Admin info 1479 // Admin info
1473 settings.settings.amiibo_initialized.Assign(1); 1480 settings.settings.amiibo_initialized.Assign(1);
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index aed12a7f8..f96d21220 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -6,7 +6,9 @@
6#include <array> 6#include <array>
7 7
8#include "common/swap.h" 8#include "common/swap.h"
9#include "core/hle/service/mii/types.h" 9#include "core/hle/service/mii/types/char_info.h"
10#include "core/hle/service/mii/types/store_data.h"
11#include "core/hle/service/mii/types/ver3_store_data.h"
10#include "core/hle/service/nfc/nfc_types.h" 12#include "core/hle/service/nfc/nfc_types.h"
11 13
12namespace Service::NFP { 14namespace Service::NFP {
@@ -322,7 +324,7 @@ static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
322 324
323// This is nn::nfp::RegisterInfoPrivate 325// This is nn::nfp::RegisterInfoPrivate
324struct RegisterInfoPrivate { 326struct RegisterInfoPrivate {
325 Service::Mii::MiiStoreData mii_store_data; 327 Service::Mii::StoreData mii_store_data;
326 WriteDate creation_date; 328 WriteDate creation_date;
327 AmiiboName amiibo_name; 329 AmiiboName amiibo_name;
328 u8 font_region; 330 u8 font_region;
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp
index a51ca5444..0ca05257e 100644
--- a/src/core/hle/service/nvdrv/core/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/core/nvmap.cpp
@@ -160,8 +160,8 @@ u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
160 u32 address{}; 160 u32 address{};
161 auto& smmu_allocator = host1x.Allocator(); 161 auto& smmu_allocator = host1x.Allocator();
162 auto& smmu_memory_manager = host1x.MemoryManager(); 162 auto& smmu_memory_manager = host1x.MemoryManager();
163 while (!(address = 163 while ((address = smmu_allocator.Allocate(
164 smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) { 164 static_cast<u32>(handle_description->aligned_size))) == 0) {
165 // Free handles until the allocation succeeds 165 // Free handles until the allocation succeeds
166 std::scoped_lock queueLock(unmap_queue_lock); 166 std::scoped_lock queueLock(unmap_queue_lock);
167 if (auto freeHandleDesc{unmap_queue.front()}) { 167 if (auto freeHandleDesc{unmap_queue.front()}) {
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp
index bac21752a..491b76d48 100644
--- a/src/core/hle/service/sockets/nsd.cpp
+++ b/src/core/hle/service/sockets/nsd.cpp
@@ -19,6 +19,12 @@ enum class ServerEnvironmentType : u8 {
19 Dp, 19 Dp,
20}; 20};
21 21
22// This is nn::nsd::EnvironmentIdentifier
23struct EnvironmentIdentifier {
24 std::array<u8, 8> identifier;
25};
26static_assert(sizeof(EnvironmentIdentifier) == 0x8);
27
22NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 28NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
23 // clang-format off 29 // clang-format off
24 static const FunctionInfo functions[] = { 30 static const FunctionInfo functions[] = {
@@ -101,8 +107,9 @@ void NSD::ResolveEx(HLERequestContext& ctx) {
101} 107}
102 108
103void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) { 109void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) {
104 const std::string environment_identifier = "lp1"; 110 constexpr EnvironmentIdentifier lp1 = {
105 ctx.WriteBuffer(environment_identifier); 111 .identifier = {'l', 'p', '1', '\0', '\0', '\0', '\0', '\0'}};
112 ctx.WriteBuffer(lp1);
106 113
107 IPC::ResponseBuilder rb{ctx, 2}; 114 IPC::ResponseBuilder rb{ctx, 2};
108 rb.Push(ResultSuccess); 115 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index 22e4a6f49..c657c4efd 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -150,6 +150,12 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
150 const std::string host = Common::StringFromBuffer(host_buffer); 150 const std::string host = Common::StringFromBuffer(host_buffer);
151 // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. 151 // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
152 152
153 // Prevent resolution of Nintendo servers
154 if (host.find("srv.nintendo.net") != std::string::npos) {
155 LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
156 return {0, GetAddrInfoError::AGAIN};
157 }
158
153 auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt); 159 auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
154 if (!res.has_value()) { 160 if (!res.has_value()) {
155 return {0, Translate(res.error())}; 161 return {0, Translate(res.error())};
@@ -261,6 +267,12 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
261 const auto host_buffer = ctx.ReadBuffer(0); 267 const auto host_buffer = ctx.ReadBuffer(0);
262 const std::string host = Common::StringFromBuffer(host_buffer); 268 const std::string host = Common::StringFromBuffer(host_buffer);
263 269
270 // Prevent resolution of Nintendo servers
271 if (host.find("srv.nintendo.net") != std::string::npos) {
272 LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
273 return {0, GetAddrInfoError::AGAIN};
274 }
275
264 std::optional<std::string> service = std::nullopt; 276 std::optional<std::string> service = std::nullopt;
265 if (ctx.CanReadBuffer(1)) { 277 if (ctx.CanReadBuffer(1)) {
266 const std::span<const u8> service_buffer = ctx.ReadBuffer(1); 278 const std::span<const u8> service_buffer = ctx.ReadBuffer(1);
diff --git a/src/core/hle/service/ssl/ssl_backend_schannel.cpp b/src/core/hle/service/ssl/ssl_backend_schannel.cpp
index d834a0c1f..212057cfc 100644
--- a/src/core/hle/service/ssl/ssl_backend_schannel.cpp
+++ b/src/core/hle/service/ssl/ssl_backend_schannel.cpp
@@ -477,7 +477,8 @@ public:
477 return ResultInternalError; 477 return ResultInternalError;
478 } 478 }
479 PCCERT_CONTEXT some_cert = nullptr; 479 PCCERT_CONTEXT some_cert = nullptr;
480 while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert))) { 480 while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert)) !=
481 nullptr) {
481 out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded), 482 out_certs->emplace_back(static_cast<u8*>(some_cert->pbCertEncoded),
482 static_cast<u8*>(some_cert->pbCertEncoded) + 483 static_cast<u8*>(some_cert->pbCertEncoded) +
483 some_cert->cbCertEncoded); 484 some_cert->cbCertEncoded);
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 6bb02393c..2eb978379 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -217,7 +217,7 @@ private:
217 IPC::ResponseBuilder rb{ctx, 6}; 217 IPC::ResponseBuilder rb{ctx, 6};
218 rb.Push(ResultSuccess); 218 rb.Push(ResultSuccess);
219 219
220 if (Settings::values.use_docked_mode.GetValue()) { 220 if (Settings::IsDockedMode()) {
221 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); 221 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
222 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); 222 rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
223 } else { 223 } else {
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 07c65dc1a..b6e355622 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -108,7 +108,7 @@ std::string GetFileTypeString(FileType type) {
108 return "unknown"; 108 return "unknown";
109} 109}
110 110
111constexpr std::array<const char*, 66> RESULT_MESSAGES{ 111constexpr std::array<const char*, 68> RESULT_MESSAGES{
112 "The operation completed successfully.", 112 "The operation completed successfully.",
113 "The loader requested to load is already loaded.", 113 "The loader requested to load is already loaded.",
114 "The operation is not implemented.", 114 "The operation is not implemented.",
@@ -175,6 +175,8 @@ constexpr std::array<const char*, 66> RESULT_MESSAGES{
175 "The KIP BLZ decompression of the section failed unexpectedly.", 175 "The KIP BLZ decompression of the section failed unexpectedly.",
176 "The INI file has a bad header.", 176 "The INI file has a bad header.",
177 "The INI file contains more than the maximum allowable number of KIP files.", 177 "The INI file contains more than the maximum allowable number of KIP files.",
178 "Integrity verification could not be performed for this file.",
179 "Integrity verification failed.",
178}; 180};
179 181
180std::string GetResultStatusString(ResultStatus status) { 182std::string GetResultStatusString(ResultStatus status) {
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 721eb8e8c..b4828f7cd 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
6#include <iosfwd> 7#include <iosfwd>
7#include <memory> 8#include <memory>
8#include <optional> 9#include <optional>
@@ -132,6 +133,8 @@ enum class ResultStatus : u16 {
132 ErrorBLZDecompressionFailed, 133 ErrorBLZDecompressionFailed,
133 ErrorBadINIHeader, 134 ErrorBadINIHeader,
134 ErrorINITooManyKIPs, 135 ErrorINITooManyKIPs,
136 ErrorIntegrityVerificationNotImplemented,
137 ErrorIntegrityVerificationFailed,
135}; 138};
136 139
137std::string GetResultStatusString(ResultStatus status); 140std::string GetResultStatusString(ResultStatus status);
@@ -170,6 +173,13 @@ public:
170 virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0; 173 virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0;
171 174
172 /** 175 /**
176 * Try to verify the integrity of the file.
177 */
178 virtual ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) {
179 return ResultStatus::ErrorIntegrityVerificationNotImplemented;
180 }
181
182 /**
173 * Get the code (typically .code section) of the application 183 * Get the code (typically .code section) of the application
174 * 184 *
175 * @param[out] buffer Reference to buffer to store data 185 * @param[out] buffer Reference to buffer to store data
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 09d40e695..4feb6968a 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -3,6 +3,8 @@
3 3
4#include <utility> 4#include <utility>
5 5
6#include "common/hex_util.h"
7#include "common/scope_exit.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
8#include "core/file_sys/nca_metadata.h" 10#include "core/file_sys/nca_metadata.h"
@@ -12,6 +14,7 @@
12#include "core/hle/service/filesystem/filesystem.h" 14#include "core/hle/service/filesystem/filesystem.h"
13#include "core/loader/deconstructed_rom_directory.h" 15#include "core/loader/deconstructed_rom_directory.h"
14#include "core/loader/nca.h" 16#include "core/loader/nca.h"
17#include "mbedtls/sha256.h"
15 18
16namespace Loader { 19namespace Loader {
17 20
@@ -80,6 +83,79 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S
80 return load_result; 83 return load_result;
81} 84}
82 85
86ResultStatus AppLoader_NCA::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) {
87 using namespace Common::Literals;
88
89 constexpr size_t NcaFileNameWithHashLength = 36;
90 constexpr size_t NcaFileNameHashLength = 32;
91 constexpr size_t NcaSha256HashLength = 32;
92 constexpr size_t NcaSha256HalfHashLength = NcaSha256HashLength / 2;
93
94 // Get the file name.
95 const auto name = file->GetName();
96
97 // We won't try to verify meta NCAs.
98 if (name.ends_with(".cnmt.nca")) {
99 return ResultStatus::Success;
100 }
101
102 // Check if we can verify this file. NCAs should be named after their hashes.
103 if (!name.ends_with(".nca") || name.size() != NcaFileNameWithHashLength) {
104 LOG_WARNING(Loader, "Unable to validate NCA with name {}", name);
105 return ResultStatus::ErrorIntegrityVerificationNotImplemented;
106 }
107
108 // Get the expected truncated hash of the NCA.
109 const auto input_hash =
110 Common::HexStringToVector(file->GetName().substr(0, NcaFileNameHashLength), false);
111
112 // Declare buffer to read into.
113 std::vector<u8> buffer(4_MiB);
114
115 // Initialize sha256 verification context.
116 mbedtls_sha256_context ctx;
117 mbedtls_sha256_init(&ctx);
118 mbedtls_sha256_starts_ret(&ctx, 0);
119
120 // Ensure we maintain a clean state on exit.
121 SCOPE_EXIT({ mbedtls_sha256_free(&ctx); });
122
123 // Declare counters.
124 const size_t total_size = file->GetSize();
125 size_t processed_size = 0;
126
127 // Begin iterating the file.
128 while (processed_size < total_size) {
129 // Refill the buffer.
130 const size_t intended_read_size = std::min(buffer.size(), total_size - processed_size);
131 const size_t read_size = file->Read(buffer.data(), intended_read_size, processed_size);
132
133 // Update the hash function with the buffer contents.
134 mbedtls_sha256_update_ret(&ctx, buffer.data(), read_size);
135
136 // Update counters.
137 processed_size += read_size;
138
139 // Call the progress function.
140 if (!progress_callback(processed_size, total_size)) {
141 return ResultStatus::ErrorIntegrityVerificationFailed;
142 }
143 }
144
145 // Finalize context and compute the output hash.
146 std::array<u8, NcaSha256HashLength> output_hash;
147 mbedtls_sha256_finish_ret(&ctx, output_hash.data());
148
149 // Compare to expected.
150 if (std::memcmp(input_hash.data(), output_hash.data(), NcaSha256HalfHashLength) != 0) {
151 LOG_ERROR(Loader, "NCA hash mismatch detected for file {}", name);
152 return ResultStatus::ErrorIntegrityVerificationFailed;
153 }
154
155 // File verified.
156 return ResultStatus::Success;
157}
158
83ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { 159ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
84 if (nca == nullptr) { 160 if (nca == nullptr) {
85 return ResultStatus::ErrorNotInitialized; 161 return ResultStatus::ErrorNotInitialized;
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index cf356ce63..96779e27f 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -39,6 +39,8 @@ public:
39 39
40 LoadResult Load(Kernel::KProcess& process, Core::System& system) override; 40 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
41 41
42 ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) override;
43
42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 44 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override; 45 ResultStatus ReadProgramId(u64& out_program_id) override;
44 46
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index f9b2549a3..fe2af1ae6 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -117,6 +117,42 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
117 return result; 117 return result;
118} 118}
119 119
120ResultStatus AppLoader_NSP::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) {
121 // Extracted-type NSPs can't be verified.
122 if (nsp->IsExtractedType()) {
123 return ResultStatus::ErrorIntegrityVerificationNotImplemented;
124 }
125
126 // Get list of all NCAs.
127 const auto ncas = nsp->GetNCAsCollapsed();
128
129 size_t total_size = 0;
130 size_t processed_size = 0;
131
132 // Loop over NCAs, collecting the total size to verify.
133 for (const auto& nca : ncas) {
134 total_size += nca->GetBaseFile()->GetSize();
135 }
136
137 // Loop over NCAs again, verifying each.
138 for (const auto& nca : ncas) {
139 AppLoader_NCA loader_nca(nca->GetBaseFile());
140
141 const auto NcaProgressCallback = [&](size_t nca_processed_size, size_t nca_total_size) {
142 return progress_callback(processed_size + nca_processed_size, total_size);
143 };
144
145 const auto verification_result = loader_nca.VerifyIntegrity(NcaProgressCallback);
146 if (verification_result != ResultStatus::Success) {
147 return verification_result;
148 }
149
150 processed_size += nca->GetBaseFile()->GetSize();
151 }
152
153 return ResultStatus::Success;
154}
155
120ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) { 156ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) {
121 return secondary_loader->ReadRomFS(out_file); 157 return secondary_loader->ReadRomFS(out_file);
122} 158}
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 79df4586a..7ce436c67 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -45,6 +45,8 @@ public:
45 45
46 LoadResult Load(Kernel::KProcess& process, Core::System& system) override; 46 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
47 47
48 ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) override;
49
48 ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; 50 ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
49 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; 51 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
50 ResultStatus ReadProgramId(u64& out_program_id) override; 52 ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 3a76bc788..12d72c380 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -85,6 +85,40 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::KProcess& process, Core::S
85 return result; 85 return result;
86} 86}
87 87
88ResultStatus AppLoader_XCI::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) {
89 // Verify secure partition, as it is the only thing we can process.
90 auto secure_partition = xci->GetSecurePartitionNSP();
91
92 // Get list of all NCAs.
93 const auto ncas = secure_partition->GetNCAsCollapsed();
94
95 size_t total_size = 0;
96 size_t processed_size = 0;
97
98 // Loop over NCAs, collecting the total size to verify.
99 for (const auto& nca : ncas) {
100 total_size += nca->GetBaseFile()->GetSize();
101 }
102
103 // Loop over NCAs again, verifying each.
104 for (const auto& nca : ncas) {
105 AppLoader_NCA loader_nca(nca->GetBaseFile());
106
107 const auto NcaProgressCallback = [&](size_t nca_processed_size, size_t nca_total_size) {
108 return progress_callback(processed_size + nca_processed_size, total_size);
109 };
110
111 const auto verification_result = loader_nca.VerifyIntegrity(NcaProgressCallback);
112 if (verification_result != ResultStatus::Success) {
113 return verification_result;
114 }
115
116 processed_size += nca->GetBaseFile()->GetSize();
117 }
118
119 return ResultStatus::Success;
120}
121
88ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& out_file) { 122ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& out_file) {
89 return nca_loader->ReadRomFS(out_file); 123 return nca_loader->ReadRomFS(out_file);
90} 124}
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index ff05e6f62..b02e136d3 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -45,6 +45,8 @@ public:
45 45
46 LoadResult Load(Kernel::KProcess& process, Core::System& system) override; 46 LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
47 47
48 ResultStatus VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) override;
49
48 ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override; 50 ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) override;
49 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; 51 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
50 ResultStatus ReadProgramId(u64& out_program_id) override; 52 ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 62b3f6636..c26179e03 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -14,6 +14,7 @@
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15 15
16#include "common/settings.h" 16#include "common/settings.h"
17#include "common/settings_enums.h"
17#include "core/file_sys/control_metadata.h" 18#include "core/file_sys/control_metadata.h"
18#include "core/file_sys/patch_manager.h" 19#include "core/file_sys/patch_manager.h"
19#include "core/loader/loader.h" 20#include "core/loader/loader.h"
@@ -275,7 +276,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
275 static_cast<u32>(Settings::values.shader_backend.GetValue())); 276 static_cast<u32>(Settings::values.shader_backend.GetValue()));
276 AddField(field_type, "Renderer_UseAsynchronousShaders", 277 AddField(field_type, "Renderer_UseAsynchronousShaders",
277 Settings::values.use_asynchronous_shaders.GetValue()); 278 Settings::values.use_asynchronous_shaders.GetValue());
278 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue()); 279 AddField(field_type, "System_UseDockedMode", Settings::IsDockedMode());
279} 280}
280 281
281bool TelemetrySession::SubmitTestcase() { 282bool TelemetrySession::SubmitTestcase() {
diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp
index d707dabe2..93038f161 100644
--- a/src/dedicated_room/yuzu_room.cpp
+++ b/src/dedicated_room/yuzu_room.cpp
@@ -368,9 +368,9 @@ int main(int argc, char** argv) {
368 if (auto room = network.GetRoom().lock()) { 368 if (auto room = network.GetRoom().lock()) {
369 AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game, 369 AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game,
370 .id = preferred_game_id}; 370 .id = preferred_game_id};
371 if (!room->Create(room_name, room_description, bind_address, port, password, max_members, 371 if (!room->Create(room_name, room_description, bind_address, static_cast<u16>(port),
372 username, preferred_game_info, std::move(verify_backend), ban_list, 372 password, max_members, username, preferred_game_info,
373 enable_yuzu_mods)) { 373 std::move(verify_backend), ban_list, enable_yuzu_mods)) {
374 LOG_INFO(Network, "Failed to create room: "); 374 LOG_INFO(Network, "Failed to create room: ");
375 return -1; 375 return -1;
376 } 376 }
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 322c29065..5c127c8ef 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -37,8 +37,6 @@ add_library(input_common STATIC
37 37
38if (MSVC) 38if (MSVC)
39 target_compile_options(input_common PRIVATE 39 target_compile_options(input_common PRIVATE
40 /W4
41
42 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 40 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
43 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 41 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
44 /we4800 # Implicit conversion from 'type' to bool. Possible information loss 42 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 870e76ab0..188e862d7 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -835,15 +835,15 @@ public:
835 return input_engine->SupportsNfc(identifier); 835 return input_engine->SupportsNfc(identifier);
836 } 836 }
837 837
838 Common::Input::NfcState StartNfcPolling() { 838 Common::Input::NfcState StartNfcPolling() override {
839 return input_engine->StartNfcPolling(identifier); 839 return input_engine->StartNfcPolling(identifier);
840 } 840 }
841 841
842 Common::Input::NfcState StopNfcPolling() { 842 Common::Input::NfcState StopNfcPolling() override {
843 return input_engine->StopNfcPolling(identifier); 843 return input_engine->StopNfcPolling(identifier);
844 } 844 }
845 845
846 Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) { 846 Common::Input::NfcState ReadAmiiboData(std::vector<u8>& out_data) override {
847 return input_engine->ReadAmiiboData(identifier, out_data); 847 return input_engine->ReadAmiiboData(identifier, out_data);
848 } 848 }
849 849
@@ -852,11 +852,11 @@ public:
852 } 852 }
853 853
854 Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request, 854 Common::Input::NfcState ReadMifareData(const Common::Input::MifareRequest& request,
855 Common::Input::MifareRequest& out_data) { 855 Common::Input::MifareRequest& out_data) override {
856 return input_engine->ReadMifareData(identifier, request, out_data); 856 return input_engine->ReadMifareData(identifier, request, out_data);
857 } 857 }
858 858
859 Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) { 859 Common::Input::NfcState WriteMifareData(const Common::Input::MifareRequest& request) override {
860 return input_engine->WriteMifareData(identifier, request); 860 return input_engine->WriteMifareData(identifier, request);
861 } 861 }
862 862
diff --git a/src/network/room.cpp b/src/network/room.cpp
index e456ea09c..d87db37de 100644
--- a/src/network/room.cpp
+++ b/src/network/room.cpp
@@ -805,7 +805,7 @@ IPv4Address Room::RoomImpl::GenerateFakeIPAddress() {
805 std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE 805 std::uniform_int_distribution<> dis(0x01, 0xFE); // Random byte between 1 and 0xFE
806 do { 806 do {
807 for (std::size_t i = 2; i < result_ip.size(); ++i) { 807 for (std::size_t i = 2; i < result_ip.size(); ++i) {
808 result_ip[i] = dis(random_gen); 808 result_ip[i] = static_cast<u8>(dis(random_gen));
809 } 809 }
810 } while (!IsValidFakeIPAddress(result_ip)); 810 } while (!IsValidFakeIPAddress(result_ip));
811 811
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 07e75f9d8..83b763447 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -245,8 +245,6 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit)
245 245
246if (MSVC) 246if (MSVC)
247 target_compile_options(shader_recompiler PRIVATE 247 target_compile_options(shader_recompiler PRIVATE
248 /W4
249
250 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 248 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
251 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 249 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
252 /we4800 # Implicit conversion from 'type' to bool. Possible information loss 250 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index 3ad668a47..d9872ecc2 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -558,7 +558,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
558 if (multi_component) { 558 if (multi_component) {
559 if (info.num_derivates >= 3) { 559 if (info.num_derivates >= 3) {
560 const auto offset_vec{ctx.var_alloc.Consume(offset)}; 560 const auto offset_vec{ctx.var_alloc.Consume(offset)};
561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yz, {}.y));", texel, texture, 561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture,
562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); 562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec);
563 return; 563 return;
564 } 564 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 7d901c04b..34240b36f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -91,6 +91,34 @@ public:
91 } 91 }
92 } 92 }
93 93
94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2,
95 Id offset, Id lod_clamp) {
96 if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) {
97 throw LogicError("Derivates must be present");
98 }
99 boost::container::static_vector<Id, 3> deriv_1_accum{
100 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0),
101 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2),
102 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0),
103 };
104 boost::container::static_vector<Id, 3> deriv_2_accum{
105 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1),
106 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3),
107 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1),
108 };
109 const Id derivates_id1{ctx.OpCompositeConstruct(
110 ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})};
111 const Id derivates_id2{ctx.OpCompositeConstruct(
112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
113 Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2);
114 if (Sirit::ValidId(offset)) {
115 Add(spv::ImageOperandsMask::Offset, offset);
116 }
117 if (has_lod_clamp) {
118 Add(spv::ImageOperandsMask::MinLod, lod_clamp);
119 }
120 }
121
94 std::span<const Id> Span() const noexcept { 122 std::span<const Id> Span() const noexcept {
95 return std::span{operands.data(), operands.size()}; 123 return std::span{operands.data(), operands.size()};
96 } 124 }
@@ -524,8 +552,11 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
524Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 552Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
525 Id derivates, Id offset, Id lod_clamp) { 553 Id derivates, Id offset, Id lod_clamp) {
526 const auto info{inst->Flags<IR::TextureInstInfo>()}; 554 const auto info{inst->Flags<IR::TextureInstInfo>()};
527 const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, 555 const auto operands =
528 offset, lod_clamp); 556 info.num_derivates == 3
557 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp)
558 : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset,
559 lod_clamp);
529 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 560 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
530 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], 561 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
531 Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); 562 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index bec5db173..238fb40e3 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -74,6 +74,11 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
74 throw InvalidArgument("Invalid image format {}", format); 74 throw InvalidArgument("Invalid image format {}", format);
75} 75}
76 76
77spv::ImageFormat GetImageFormatForBuffer(ImageFormat format) {
78 const auto spv_format = GetImageFormat(format);
79 return spv_format == spv::ImageFormat::Unknown ? spv::ImageFormat::R32ui : spv_format;
80}
81
77Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { 82Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
78 const spv::ImageFormat format{GetImageFormat(desc.format)}; 83 const spv::ImageFormat format{GetImageFormat(desc.format)};
79 const Id type{ctx.U32[1]}; 84 const Id type{ctx.U32[1]};
@@ -1271,7 +1276,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
1271 if (desc.count != 1) { 1276 if (desc.count != 1) {
1272 throw NotImplementedException("Array of image buffers"); 1277 throw NotImplementedException("Array of image buffers");
1273 } 1278 }
1274 const spv::ImageFormat format{GetImageFormat(desc.format)}; 1279 const spv::ImageFormat format{GetImageFormatForBuffer(desc.format)};
1275 const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; 1280 const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
1276 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; 1281 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1277 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1282 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
index 753c62098..e593132e6 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
@@ -161,7 +161,8 @@ enum class SpecialRegister : u64 {
161 LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY"); 161 LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY");
162 return ir.Imm32(0); // This is the default value hardware returns. 162 return ir.Imm32(0); // This is the default value hardware returns.
163 default: 163 default:
164 throw NotImplementedException("S2R special register {}", special_register); 164 LOG_CRITICAL(Shader, "(STUBBED) Special register {}", special_register);
165 return ir.Imm32(0); // This is the default value hardware returns.
165 } 166 }
166} 167}
167} // Anonymous namespace 168} // Anonymous namespace
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp
index e85f9977b..b6e3bc875 100644
--- a/src/tests/common/ring_buffer.cpp
+++ b/src/tests/common/ring_buffer.cpp
@@ -55,7 +55,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") {
55 // Pushing more values than space available should partially succeed. 55 // Pushing more values than space available should partially succeed.
56 { 56 {
57 std::vector<char> to_push(6); 57 std::vector<char> to_push(6);
58 std::iota(to_push.begin(), to_push.end(), 88); 58 std::iota(to_push.begin(), to_push.end(), static_cast<char>(88));
59 const std::size_t count = buf.Push(to_push); 59 const std::size_t count = buf.Push(to_push);
60 REQUIRE(count == 3U); 60 REQUIRE(count == 3U);
61 } 61 }
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f0f450edb..8be7bd594 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -289,8 +289,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad
289 MarkWrittenBuffer(buffer_id, *cpu_addr, size); 289 MarkWrittenBuffer(buffer_id, *cpu_addr, size);
290 break; 290 break;
291 case ObtainBufferOperation::DiscardWrite: { 291 case ObtainBufferOperation::DiscardWrite: {
292 IntervalType interval{*cpu_addr, size}; 292 VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
293 VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
294 IntervalType interval{cpu_addr_start, cpu_addr_end};
293 ClearDownload(interval); 295 ClearDownload(interval);
296 common_ranges.subtract(interval);
294 break; 297 break;
295 } 298 }
296 default: 299 default:
@@ -1159,6 +1162,11 @@ void BufferCache<P>::UpdateDrawIndirect() {
1159 .size = static_cast<u32>(size), 1162 .size = static_cast<u32>(size),
1160 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), 1163 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
1161 }; 1164 };
1165 VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
1166 VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
1167 IntervalType interval{cpu_addr_start, cpu_addr_end};
1168 ClearDownload(interval);
1169 common_ranges.subtract(interval);
1162 }; 1170 };
1163 if (current_draw_indirect->include_count) { 1171 if (current_draw_indirect->include_count) {
1164 update(current_draw_indirect->count_start_address, sizeof(u32), 1172 update(current_draw_indirect->count_start_address, sizeof(u32),
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 9f1b340a9..58ce0d8c2 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -14,6 +14,7 @@
14namespace Tegra { 14namespace Tegra {
15 15
16constexpr u32 MacroRegistersStart = 0xE00; 16constexpr u32 MacroRegistersStart = 0xE00;
17constexpr u32 ComputeInline = 0x6D;
17 18
18DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, 19DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_,
19 Control::ChannelState& channel_state_) 20 Control::ChannelState& channel_state_)
@@ -83,12 +84,35 @@ bool DmaPusher::Step() {
83 dma_state.dma_get, command_list_header.size * sizeof(u32)); 84 dma_state.dma_get, command_list_header.size * sizeof(u32));
84 } 85 }
85 } 86 }
86 Core::Memory::GpuGuestMemory<Tegra::CommandHeader, 87 const auto safe_process = [&] {
87 Core::Memory::GuestMemoryFlags::UnsafeRead> 88 Core::Memory::GpuGuestMemory<Tegra::CommandHeader,
88 headers(memory_manager, dma_state.dma_get, command_list_header.size, &command_headers); 89 Core::Memory::GuestMemoryFlags::SafeRead>
89 ProcessCommands(headers); 90 headers(memory_manager, dma_state.dma_get, command_list_header.size,
91 &command_headers);
92 ProcessCommands(headers);
93 };
94 const auto unsafe_process = [&] {
95 Core::Memory::GpuGuestMemory<Tegra::CommandHeader,
96 Core::Memory::GuestMemoryFlags::UnsafeRead>
97 headers(memory_manager, dma_state.dma_get, command_list_header.size,
98 &command_headers);
99 ProcessCommands(headers);
100 };
101 if (Settings::IsGPULevelHigh()) {
102 if (dma_state.method >= MacroRegistersStart) {
103 unsafe_process();
104 return true;
105 }
106 if (subchannel_type[dma_state.subchannel] == Engines::EngineTypes::KeplerCompute &&
107 dma_state.method == ComputeInline) {
108 unsafe_process();
109 return true;
110 }
111 safe_process();
112 return true;
113 }
114 unsafe_process();
90 } 115 }
91
92 return true; 116 return true;
93} 117}
94 118
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 8a2784cdc..c9fab2d90 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -130,8 +130,10 @@ public:
130 130
131 void DispatchCalls(); 131 void DispatchCalls();
132 132
133 void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id) { 133 void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id,
134 Engines::EngineTypes engine_type) {
134 subchannels[subchannel_id] = engine; 135 subchannels[subchannel_id] = engine;
136 subchannel_type[subchannel_id] = engine_type;
135 } 137 }
136 138
137 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); 139 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
@@ -170,6 +172,7 @@ private:
170 const bool ib_enable{true}; ///< IB mode enabled 172 const bool ib_enable{true}; ///< IB mode enabled
171 173
172 std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; 174 std::array<Engines::EngineInterface*, max_subchannels> subchannels{};
175 std::array<Engines::EngineTypes, max_subchannels> subchannel_type;
173 176
174 GPU& gpu; 177 GPU& gpu;
175 Core::System& system; 178 Core::System& system;
diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h
index 392322358..54631ee6c 100644
--- a/src/video_core/engines/engine_interface.h
+++ b/src/video_core/engines/engine_interface.h
@@ -11,6 +11,14 @@
11 11
12namespace Tegra::Engines { 12namespace Tegra::Engines {
13 13
14enum class EngineTypes : u32 {
15 KeplerCompute,
16 Maxwell3D,
17 Fermi2D,
18 MaxwellDMA,
19 KeplerMemory,
20};
21
14class EngineInterface { 22class EngineInterface {
15public: 23public:
16 virtual ~EngineInterface() = default; 24 virtual ~EngineInterface() = default;
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h
index 7242d2529..21bf8aeb4 100644
--- a/src/video_core/engines/engine_upload.h
+++ b/src/video_core/engines/engine_upload.h
@@ -69,6 +69,14 @@ public:
69 /// Binds a rasterizer to this engine. 69 /// Binds a rasterizer to this engine.
70 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); 70 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
71 71
72 GPUVAddr ExecTargetAddress() const {
73 return regs.dest.Address();
74 }
75
76 u32 GetUploadSize() const {
77 return copy_size;
78 }
79
72private: 80private:
73 void ProcessData(std::span<const u8> read_buffer); 81 void ProcessData(std::span<const u8> read_buffer);
74 82
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index a38d9528a..cd61ab222 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -43,16 +43,33 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal
43 43
44 switch (method) { 44 switch (method) {
45 case KEPLER_COMPUTE_REG_INDEX(exec_upload): { 45 case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
46 UploadInfo info{.upload_address = upload_address,
47 .exec_address = upload_state.ExecTargetAddress(),
48 .copy_size = upload_state.GetUploadSize()};
49 uploads.push_back(info);
46 upload_state.ProcessExec(regs.exec_upload.linear != 0); 50 upload_state.ProcessExec(regs.exec_upload.linear != 0);
47 break; 51 break;
48 } 52 }
49 case KEPLER_COMPUTE_REG_INDEX(data_upload): { 53 case KEPLER_COMPUTE_REG_INDEX(data_upload): {
54 upload_address = current_dma_segment;
50 upload_state.ProcessData(method_argument, is_last_call); 55 upload_state.ProcessData(method_argument, is_last_call);
51 break; 56 break;
52 } 57 }
53 case KEPLER_COMPUTE_REG_INDEX(launch): 58 case KEPLER_COMPUTE_REG_INDEX(launch): {
59 const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address();
60
61 for (auto& data : uploads) {
62 const GPUVAddr offset = data.exec_address - launch_desc_loc;
63 if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) &&
64 memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) {
65 indirect_compute = {data.upload_address};
66 }
67 }
68 uploads.clear();
54 ProcessLaunch(); 69 ProcessLaunch();
70 indirect_compute = std::nullopt;
55 break; 71 break;
72 }
56 default: 73 default:
57 break; 74 break;
58 } 75 }
@@ -62,6 +79,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun
62 u32 methods_pending) { 79 u32 methods_pending) {
63 switch (method) { 80 switch (method) {
64 case KEPLER_COMPUTE_REG_INDEX(data_upload): 81 case KEPLER_COMPUTE_REG_INDEX(data_upload):
82 upload_address = current_dma_segment;
65 upload_state.ProcessData(base_start, amount); 83 upload_state.ProcessData(base_start, amount);
66 return; 84 return;
67 default: 85 default:
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index 2092e685f..735e05fb4 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <cstddef> 7#include <cstddef>
8#include <optional>
8#include <vector> 9#include <vector>
9#include "common/bit_field.h" 10#include "common/bit_field.h"
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
@@ -36,6 +37,9 @@ namespace Tegra::Engines {
36#define KEPLER_COMPUTE_REG_INDEX(field_name) \ 37#define KEPLER_COMPUTE_REG_INDEX(field_name) \
37 (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) 38 (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32))
38 39
40#define LAUNCH_REG_INDEX(field_name) \
41 (offsetof(Tegra::Engines::KeplerCompute::LaunchParams, field_name) / sizeof(u32))
42
39class KeplerCompute final : public EngineInterface { 43class KeplerCompute final : public EngineInterface {
40public: 44public:
41 explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); 45 explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager);
@@ -201,6 +205,10 @@ public:
201 void CallMultiMethod(u32 method, const u32* base_start, u32 amount, 205 void CallMultiMethod(u32 method, const u32* base_start, u32 amount,
202 u32 methods_pending) override; 206 u32 methods_pending) override;
203 207
208 std::optional<GPUVAddr> GetIndirectComputeAddress() const {
209 return indirect_compute;
210 }
211
204private: 212private:
205 void ProcessLaunch(); 213 void ProcessLaunch();
206 214
@@ -216,6 +224,15 @@ private:
216 MemoryManager& memory_manager; 224 MemoryManager& memory_manager;
217 VideoCore::RasterizerInterface* rasterizer = nullptr; 225 VideoCore::RasterizerInterface* rasterizer = nullptr;
218 Upload::State upload_state; 226 Upload::State upload_state;
227 GPUVAddr upload_address;
228
229 struct UploadInfo {
230 GPUVAddr upload_address;
231 GPUVAddr exec_address;
232 u32 copy_size;
233 };
234 std::vector<UploadInfo> uploads;
235 std::optional<GPUVAddr> indirect_compute{};
219}; 236};
220 237
221#define ASSERT_REG_POSITION(field_name, position) \ 238#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index c3696096d..06e349e43 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -257,6 +257,7 @@ u32 Maxwell3D::GetMaxCurrentVertices() {
257 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 257 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
258 num_vertices = std::max( 258 num_vertices = std::max(
259 num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value())); 259 num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value()));
260 break;
260 } 261 }
261 return num_vertices; 262 return num_vertices;
262} 263}
@@ -269,10 +270,13 @@ size_t Maxwell3D::EstimateIndexBufferSize() {
269 std::numeric_limits<u32>::max()}; 270 std::numeric_limits<u32>::max()};
270 const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); 271 const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
271 const size_t log2_byte_size = Common::Log2Ceil64(byte_size); 272 const size_t log2_byte_size = Common::Log2Ceil64(byte_size);
273 const size_t cap{GetMaxCurrentVertices() * 3 * byte_size};
274 const size_t lower_cap =
275 std::min<size_t>(static_cast<size_t>(end_address - start_address), cap);
272 return std::min<size_t>( 276 return std::min<size_t>(
273 memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) / 277 memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) /
274 byte_size, 278 byte_size,
275 static_cast<size_t>(end_address - start_address)); 279 lower_cap);
276} 280}
277 281
278u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { 282u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
index 7718a09b3..6de2543b7 100644
--- a/src/video_core/engines/puller.cpp
+++ b/src/video_core/engines/puller.cpp
@@ -34,19 +34,24 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
34 bound_engines[method_call.subchannel] = engine_id; 34 bound_engines[method_call.subchannel] = engine_id;
35 switch (engine_id) { 35 switch (engine_id) {
36 case EngineID::FERMI_TWOD_A: 36 case EngineID::FERMI_TWOD_A:
37 dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel); 37 dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel,
38 EngineTypes::Fermi2D);
38 break; 39 break;
39 case EngineID::MAXWELL_B: 40 case EngineID::MAXWELL_B:
40 dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel); 41 dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel,
42 EngineTypes::Maxwell3D);
41 break; 43 break;
42 case EngineID::KEPLER_COMPUTE_B: 44 case EngineID::KEPLER_COMPUTE_B:
43 dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel); 45 dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel,
46 EngineTypes::KeplerCompute);
44 break; 47 break;
45 case EngineID::MAXWELL_DMA_COPY_A: 48 case EngineID::MAXWELL_DMA_COPY_A:
46 dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel); 49 dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel,
50 EngineTypes::MaxwellDMA);
47 break; 51 break;
48 case EngineID::KEPLER_INLINE_TO_MEMORY_B: 52 case EngineID::KEPLER_INLINE_TO_MEMORY_B:
49 dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel); 53 dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel,
54 EngineTypes::KeplerMemory);
50 break; 55 break;
51 default: 56 default:
52 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); 57 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp
index 220cce28a..8d7da50fc 100644
--- a/src/video_core/host1x/codecs/codec.cpp
+++ b/src/video_core/host1x/codecs/codec.cpp
@@ -319,6 +319,7 @@ void Codec::Decode() {
319 LOG_WARNING(Service_NVDRV, "Zero width or height in frame"); 319 LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
320 return; 320 return;
321 } 321 }
322 bool is_interlaced = initial_frame->interlaced_frame != 0;
322 if (av_codec_ctx->hw_device_ctx) { 323 if (av_codec_ctx->hw_device_ctx) {
323 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter}; 324 final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
324 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed"); 325 ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
@@ -334,7 +335,7 @@ void Codec::Decode() {
334 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format); 335 UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
335 return; 336 return;
336 } 337 }
337 if (!final_frame->interlaced_frame) { 338 if (!is_interlaced) {
338 av_frames.push(std::move(final_frame)); 339 av_frames.push(std::move(final_frame));
339 } else { 340 } else {
340 if (!filters_initialized) { 341 if (!filters_initialized) {
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index 905505ca1..5d0bb9cc4 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -27,14 +27,24 @@ MICROPROFILE_DEFINE(MacroHLE, "GPU", "Execute macro HLE", MP_RGB(128, 192, 192))
27 27
28namespace Tegra { 28namespace Tegra {
29 29
30static void Dump(u64 hash, std::span<const u32> code) { 30static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
31 const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)}; 31 const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
32 const auto macro_dir{base_dir / "macros"}; 32 const auto macro_dir{base_dir / "macros"};
33 if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) { 33 if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
34 LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories"); 34 LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
35 return; 35 return;
36 } 36 }
37 const auto name{macro_dir / fmt::format("{:016x}.macro", hash)}; 37 auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
38
39 if (decompiled) {
40 auto new_name{macro_dir / fmt::format("decompiled_{:016x}.macro", hash)};
41 if (Common::FS::Exists(name)) {
42 (void)Common::FS::RenameFile(name, new_name);
43 return;
44 }
45 name = new_name;
46 }
47
38 std::fstream macro_file(name, std::ios::out | std::ios::binary); 48 std::fstream macro_file(name, std::ios::out | std::ios::binary);
39 if (!macro_file) { 49 if (!macro_file) {
40 LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}", 50 LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
@@ -90,9 +100,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
90 if (!mid_method.has_value()) { 100 if (!mid_method.has_value()) {
91 cache_info.lle_program = Compile(macro_code->second); 101 cache_info.lle_program = Compile(macro_code->second);
92 cache_info.hash = Common::HashValue(macro_code->second); 102 cache_info.hash = Common::HashValue(macro_code->second);
93 if (Settings::values.dump_macros) {
94 Dump(cache_info.hash, macro_code->second);
95 }
96 } else { 103 } else {
97 const auto& macro_cached = uploaded_macro_code[mid_method.value()]; 104 const auto& macro_cached = uploaded_macro_code[mid_method.value()];
98 const auto rebased_method = method - mid_method.value(); 105 const auto rebased_method = method - mid_method.value();
@@ -102,9 +109,6 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
102 code.size() * sizeof(u32)); 109 code.size() * sizeof(u32));
103 cache_info.hash = Common::HashValue(code); 110 cache_info.hash = Common::HashValue(code);
104 cache_info.lle_program = Compile(code); 111 cache_info.lle_program = Compile(code);
105 if (Settings::values.dump_macros) {
106 Dump(cache_info.hash, code);
107 }
108 } 112 }
109 113
110 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); 114 auto hle_program = hle_macros->GetHLEProgram(cache_info.hash);
@@ -117,6 +121,10 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
117 MICROPROFILE_SCOPE(MacroHLE); 121 MICROPROFILE_SCOPE(MacroHLE);
118 cache_info.hle_program->Execute(parameters, method); 122 cache_info.hle_program->Execute(parameters, method);
119 } 123 }
124
125 if (Settings::values.dump_macros) {
126 Dump(cache_info.hash, macro_code->second, cache_info.has_hle_program);
127 }
120 } 128 }
121} 129}
122 130
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 1ba31be88..dd03efecd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -380,6 +380,17 @@ void RasterizerOpenGL::DispatchCompute() {
380 pipeline->SetEngine(kepler_compute, gpu_memory); 380 pipeline->SetEngine(kepler_compute, gpu_memory);
381 pipeline->Configure(); 381 pipeline->Configure();
382 const auto& qmd{kepler_compute->launch_description}; 382 const auto& qmd{kepler_compute->launch_description};
383 auto indirect_address = kepler_compute->GetIndirectComputeAddress();
384 if (indirect_address) {
385 // DispatchIndirect
386 static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
387 const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite;
388 const auto [buffer, offset] =
389 buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op);
390 glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer->Handle());
391 glDispatchComputeIndirect(static_cast<GLintptr>(offset));
392 return;
393 }
383 glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); 394 glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z);
384 ++num_queued_commands; 395 ++num_queued_commands;
385 has_written_global_memory |= pipeline->WritesGlobalMemory(); 396 has_written_global_memory |= pipeline->WritesGlobalMemory();
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 454bb66a4..c4c30d807 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -66,21 +66,6 @@ std::string BuildCommaSeparatedExtensions(
66 return fmt::format("{}", fmt::join(available_extensions, ",")); 66 return fmt::format("{}", fmt::join(available_extensions, ","));
67} 67}
68 68
69DebugCallback MakeDebugCallback(const vk::Instance& instance, const vk::InstanceDispatch& dld) {
70 if (!Settings::values.renderer_debug) {
71 return DebugCallback{};
72 }
73 const std::optional properties = vk::EnumerateInstanceExtensionProperties(dld);
74 const auto it = std::ranges::find_if(*properties, [](const auto& prop) {
75 return std::strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, prop.extensionName) == 0;
76 });
77 if (it != properties->end()) {
78 return CreateDebugUtilsCallback(instance);
79 } else {
80 return CreateDebugReportCallback(instance);
81 }
82}
83
84} // Anonymous namespace 69} // Anonymous namespace
85 70
86Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 71Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
@@ -103,7 +88,8 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
103 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())), 88 cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary(context.get())),
104 instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, 89 instance(CreateInstance(*library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
105 Settings::values.renderer_debug.GetValue())), 90 Settings::values.renderer_debug.GetValue())),
106 debug_callback(MakeDebugCallback(instance, dld)), 91 debug_messenger(Settings::values.renderer_debug ? CreateDebugUtilsCallback(instance)
92 : vk::DebugUtilsMessenger{}),
107 surface(CreateSurface(instance, render_window.GetWindowInfo())), 93 surface(CreateSurface(instance, render_window.GetWindowInfo())),
108 device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(), 94 device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(),
109 scheduler(device, state_tracker), 95 scheduler(device, state_tracker),
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 89e98425e..590bc1c64 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -35,8 +35,6 @@ class GPU;
35 35
36namespace Vulkan { 36namespace Vulkan {
37 37
38using DebugCallback = std::variant<vk::DebugUtilsMessenger, vk::DebugReportCallback>;
39
40Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, 38Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
41 VkSurfaceKHR surface); 39 VkSurfaceKHR surface);
42 40
@@ -75,7 +73,7 @@ private:
75 vk::InstanceDispatch dld; 73 vk::InstanceDispatch dld;
76 74
77 vk::Instance instance; 75 vk::Instance instance;
78 DebugCallback debug_callback; 76 vk::DebugUtilsMessenger debug_messenger;
79 vk::SurfaceKHR surface; 77 vk::SurfaceKHR surface;
80 78
81 ScreenInfo screen_info; 79 ScreenInfo screen_info;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 60a6ac651..e15865d16 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -529,17 +529,20 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
529 buffer_handles.push_back(handle); 529 buffer_handles.push_back(handle);
530 } 530 }
531 if (device.IsExtExtendedDynamicStateSupported()) { 531 if (device.IsExtExtendedDynamicStateSupported()) {
532 scheduler.Record([bindings_ = std::move(bindings), 532 scheduler.Record([this, bindings_ = std::move(bindings),
533 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 533 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
534 cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, 534 cmdbuf.BindVertexBuffers2EXT(bindings_.min_index,
535 bindings_.max_index - bindings_.min_index, 535 std::min(bindings_.max_index - bindings_.min_index,
536 device.GetMaxVertexInputBindings()),
536 buffer_handles_.data(), bindings_.offsets.data(), 537 buffer_handles_.data(), bindings_.offsets.data(),
537 bindings_.sizes.data(), bindings_.strides.data()); 538 bindings_.sizes.data(), bindings_.strides.data());
538 }); 539 });
539 } else { 540 } else {
540 scheduler.Record([bindings_ = std::move(bindings), 541 scheduler.Record([this, bindings_ = std::move(bindings),
541 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 542 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
542 cmdbuf.BindVertexBuffers(bindings_.min_index, bindings_.max_index - bindings_.min_index, 543 cmdbuf.BindVertexBuffers(bindings_.min_index,
544 std::min(bindings_.max_index - bindings_.min_index,
545 device.GetMaxVertexInputBindings()),
543 buffer_handles_.data(), bindings_.offsets.data()); 546 buffer_handles_.data(), bindings_.offsets.data());
544 }); 547 });
545 } 548 }
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index c1314ca99..4f83a88e1 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -611,9 +611,6 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
611 611
612 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; 612 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
613 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); 613 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
614 if (Settings::values.dump_shaders) {
615 env.Dump(hash, key.unique_hashes[index]);
616 }
617 if (!uses_vertex_a || index != 1) { 614 if (!uses_vertex_a || index != 1) {
618 // Normal path 615 // Normal path
619 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); 616 programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info);
@@ -624,6 +621,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
624 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); 621 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
625 } 622 }
626 623
624 if (Settings::values.dump_shaders) {
625 env.Dump(hash, key.unique_hashes[index]);
626 }
627
627 if (programs[index].info.requires_layer_emulation) { 628 if (programs[index].info.requires_layer_emulation) {
628 layer_source_program = &programs[index]; 629 layer_source_program = &programs[index];
629 } 630 }
@@ -664,6 +665,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
664 std::move(modules), infos); 665 std::move(modules), infos);
665 666
666} catch (const Shader::Exception& exception) { 667} catch (const Shader::Exception& exception) {
668 auto hash = key.Hash();
669 size_t env_index{0};
670 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
671 if (key.unique_hashes[index] == 0) {
672 continue;
673 }
674 Shader::Environment& env{*envs[env_index]};
675 ++env_index;
676
677 const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
678 Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0);
679 env.Dump(hash, key.unique_hashes[index]);
680 }
667 LOG_ERROR(Render_Vulkan, "{}", exception.what()); 681 LOG_ERROR(Render_Vulkan, "{}", exception.what());
668 return nullptr; 682 return nullptr;
669} 683}
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 032f694bc..01e76a82c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -463,6 +463,20 @@ void RasterizerVulkan::DispatchCompute() {
463 pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); 463 pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache);
464 464
465 const auto& qmd{kepler_compute->launch_description}; 465 const auto& qmd{kepler_compute->launch_description};
466 auto indirect_address = kepler_compute->GetIndirectComputeAddress();
467 if (indirect_address) {
468 // DispatchIndirect
469 static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize;
470 const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite;
471 const auto [buffer, offset] =
472 buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op);
473 scheduler.RequestOutsideRenderPassOperationContext();
474 scheduler.Record([indirect_buffer = buffer->Handle(),
475 indirect_offset = offset](vk::CommandBuffer cmdbuf) {
476 cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset);
477 });
478 return;
479 }
466 const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; 480 const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z};
467 scheduler.RequestOutsideRenderPassOperationContext(); 481 scheduler.RequestOutsideRenderPassOperationContext();
468 scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); 482 scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); });
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.cpp b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
index 67e8065a4..448df2d3a 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.cpp
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.cpp
@@ -63,22 +63,6 @@ VkBool32 DebugUtilCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
63 return VK_FALSE; 63 return VK_FALSE;
64} 64}
65 65
66VkBool32 DebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType,
67 uint64_t object, size_t location, int32_t messageCode,
68 const char* pLayerPrefix, const char* pMessage, void* pUserData) {
69 const VkDebugReportFlagBitsEXT severity = static_cast<VkDebugReportFlagBitsEXT>(flags);
70 const std::string_view message{pMessage};
71 if (severity & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
72 LOG_CRITICAL(Render_Vulkan, "{}", message);
73 } else if (severity & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
74 LOG_WARNING(Render_Vulkan, "{}", message);
75 } else if (severity & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
76 LOG_INFO(Render_Vulkan, "{}", message);
77 } else if (severity & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
78 LOG_DEBUG(Render_Vulkan, "{}", message);
79 }
80 return VK_FALSE;
81}
82} // Anonymous namespace 66} // Anonymous namespace
83 67
84vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) { 68vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) {
@@ -98,15 +82,4 @@ vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance) {
98 }); 82 });
99} 83}
100 84
101vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance) {
102 return instance.CreateDebugReportCallback({
103 .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
104 .pNext = nullptr,
105 .flags = VK_DEBUG_REPORT_DEBUG_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
106 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
107 .pfnCallback = DebugReportCallback,
108 .pUserData = nullptr,
109 });
110}
111
112} // namespace Vulkan 85} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_debug_callback.h b/src/video_core/vulkan_common/vulkan_debug_callback.h
index a8af7b406..5e940782f 100644
--- a/src/video_core/vulkan_common/vulkan_debug_callback.h
+++ b/src/video_core/vulkan_common/vulkan_debug_callback.h
@@ -9,6 +9,4 @@ namespace Vulkan {
9 9
10vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance); 10vk::DebugUtilsMessenger CreateDebugUtilsCallback(const vk::Instance& instance);
11 11
12vk::DebugReportCallback CreateDebugReportCallback(const vk::Instance& instance);
13
14} // namespace Vulkan 12} // namespace Vulkan
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index bc16145be..180657a75 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -76,11 +76,9 @@ namespace {
76 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); 76 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
77 } 77 }
78#endif 78#endif
79 if (enable_validation) { 79 if (enable_validation &&
80 const bool debug_utils = 80 AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME})) {
81 AreExtensionsSupported(dld, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}); 81 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
82 extensions.push_back(debug_utils ? VK_EXT_DEBUG_UTILS_EXTENSION_NAME
83 : VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
84 } 82 }
85 return extensions; 83 return extensions;
86} 84}
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 78e5a248f..c3f388d89 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -92,6 +92,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
92 X(vkCmdCopyImage); 92 X(vkCmdCopyImage);
93 X(vkCmdCopyImageToBuffer); 93 X(vkCmdCopyImageToBuffer);
94 X(vkCmdDispatch); 94 X(vkCmdDispatch);
95 X(vkCmdDispatchIndirect);
95 X(vkCmdDraw); 96 X(vkCmdDraw);
96 X(vkCmdDrawIndexed); 97 X(vkCmdDrawIndexed);
97 X(vkCmdDrawIndirect); 98 X(vkCmdDrawIndirect);
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h
index c226a2a29..049fa8038 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.h
+++ b/src/video_core/vulkan_common/vulkan_wrapper.h
@@ -203,6 +203,7 @@ struct DeviceDispatch : InstanceDispatch {
203 PFN_vkCmdCopyImage vkCmdCopyImage{}; 203 PFN_vkCmdCopyImage vkCmdCopyImage{};
204 PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; 204 PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{};
205 PFN_vkCmdDispatch vkCmdDispatch{}; 205 PFN_vkCmdDispatch vkCmdDispatch{};
206 PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect{};
206 PFN_vkCmdDraw vkCmdDraw{}; 207 PFN_vkCmdDraw vkCmdDraw{};
207 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; 208 PFN_vkCmdDrawIndexed vkCmdDrawIndexed{};
208 PFN_vkCmdDrawIndirect vkCmdDrawIndirect{}; 209 PFN_vkCmdDrawIndirect vkCmdDrawIndirect{};
@@ -1209,6 +1210,10 @@ public:
1209 dld->vkCmdDispatch(handle, x, y, z); 1210 dld->vkCmdDispatch(handle, x, y, z);
1210 } 1211 }
1211 1212
1213 void DispatchIndirect(VkBuffer indirect_buffer, VkDeviceSize offset) const noexcept {
1214 dld->vkCmdDispatchIndirect(handle, indirect_buffer, offset);
1215 }
1216
1212 void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, 1217 void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask,
1213 VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, 1218 VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers,
1214 Span<VkBufferMemoryBarrier> buffer_barriers, 1219 Span<VkBufferMemoryBarrier> buffer_barriers,
diff --git a/src/web_service/verify_user_jwt.cpp b/src/web_service/verify_user_jwt.cpp
index 129eb1968..f88f67620 100644
--- a/src/web_service/verify_user_jwt.cpp
+++ b/src/web_service/verify_user_jwt.cpp
@@ -4,6 +4,7 @@
4#if defined(__GNUC__) || defined(__clang__) 4#if defined(__GNUC__) || defined(__clang__)
5#pragma GCC diagnostic push 5#pragma GCC diagnostic push
6#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 6#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
7#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // for deprecated OpenSSL functions
7#endif 8#endif
8#include <jwt/jwt.hpp> 9#include <jwt/jwt.hpp>
9#if defined(__GNUC__) || defined(__clang__) 10#if defined(__GNUC__) || defined(__clang__)
diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp
index 4988fcc83..b457a736a 100644
--- a/src/yuzu/applets/qt_amiibo_settings.cpp
+++ b/src/yuzu/applets/qt_amiibo_settings.cpp
@@ -160,7 +160,8 @@ void QtAmiiboSettingsDialog::LoadAmiiboData() {
160 } 160 }
161 161
162 const auto amiibo_name = std::string(register_info.amiibo_name.data()); 162 const auto amiibo_name = std::string(register_info.amiibo_name.data());
163 const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data()); 163 const auto owner_name =
164 Common::UTF16ToUTF8(register_info.mii_char_info.GetNickname().data.data());
164 const auto creation_date = 165 const auto creation_date =
165 QDate(register_info.creation_date.year, register_info.creation_date.month, 166 QDate(register_info.creation_date.year, register_info.creation_date.month,
166 register_info.creation_date.day); 167 register_info.creation_date.day);
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 00aafb8f8..d15559518 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -5,6 +5,8 @@
5#include <thread> 5#include <thread>
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/settings.h"
9#include "common/settings_enums.h"
8#include "common/string_util.h" 10#include "common/string_util.h"
9#include "core/core.h" 11#include "core/core.h"
10#include "core/hid/emulated_controller.h" 12#include "core/hid/emulated_controller.h"
@@ -226,9 +228,11 @@ int QtControllerSelectorDialog::exec() {
226} 228}
227 229
228void QtControllerSelectorDialog::ApplyConfiguration() { 230void QtControllerSelectorDialog::ApplyConfiguration() {
229 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); 231 const bool pre_docked_mode = Settings::IsDockedMode();
230 Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); 232 const bool docked_mode_selected = ui->radioDocked->isChecked();
231 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system); 233 Settings::values.use_docked_mode.SetValue(
234 docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
235 OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
232 236
233 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); 237 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
234 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); 238 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
@@ -616,8 +620,8 @@ void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
616 ui->radioDocked->setEnabled(!is_handheld); 620 ui->radioDocked->setEnabled(!is_handheld);
617 ui->radioUndocked->setEnabled(!is_handheld); 621 ui->radioUndocked->setEnabled(!is_handheld);
618 622
619 ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); 623 ui->radioDocked->setChecked(Settings::IsDockedMode());
620 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); 624 ui->radioUndocked->setChecked(!Settings::IsDockedMode());
621 625
622 // Also force into undocked mode if the controller type is handheld. 626 // Also force into undocked mode if the controller type is handheld.
623 if (is_handheld) { 627 if (is_handheld) {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 407988b8f..2afa72140 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -928,8 +928,8 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
928 const Layout::FramebufferLayout layout{[]() { 928 const Layout::FramebufferLayout layout{[]() {
929 u32 height = UISettings::values.screenshot_height.GetValue(); 929 u32 height = UISettings::values.screenshot_height.GetValue();
930 if (height == 0) { 930 if (height == 0) {
931 height = Settings::values.use_docked_mode.GetValue() ? Layout::ScreenDocked::Height 931 height = Settings::IsDockedMode() ? Layout::ScreenDocked::Height
932 : Layout::ScreenUndocked::Height; 932 : Layout::ScreenUndocked::Height;
933 height *= Settings::values.resolution_info.up_factor; 933 height *= Settings::values.resolution_info.up_factor;
934 } 934 }
935 const u32 width = 935 const u32 width =
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index b22c83303..1de093447 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -9,6 +9,7 @@
9#include "common/fs/path_util.h" 9#include "common/fs/path_util.h"
10#include "common/settings.h" 10#include "common/settings.h"
11#include "common/settings_common.h" 11#include "common/settings_common.h"
12#include "common/settings_enums.h"
12#include "core/core.h" 13#include "core/core.h"
13#include "core/hle/service/acc/profile_manager.h" 14#include "core/hle/service/acc/profile_manager.h"
14#include "core/hle/service/hid/controllers/npad.h" 15#include "core/hle/service/hid/controllers/npad.h"
@@ -85,9 +86,9 @@ const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_ma
85 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, 86 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
86}; 87};
87 88
88const std::map<bool, QString> Config::use_docked_mode_texts_map = { 89const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = {
89 {true, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, 90 {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
90 {false, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, 91 {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
91}; 92};
92 93
93const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = { 94const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
@@ -376,7 +377,7 @@ void Config::ReadControlValues() {
376 const auto controller_type = Settings::values.players.GetValue()[0].controller_type; 377 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
377 if (controller_type == Settings::ControllerType::Handheld) { 378 if (controller_type == Settings::ControllerType::Handheld) {
378 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); 379 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
379 Settings::values.use_docked_mode.SetValue(false); 380 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
380 } 381 }
381 382
382 if (IsCustomConfig()) { 383 if (IsCustomConfig()) {
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 0ac74c8e7..727feebfb 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -9,6 +9,7 @@
9#include <QMetaType> 9#include <QMetaType>
10#include <QVariant> 10#include <QVariant>
11#include "common/settings.h" 11#include "common/settings.h"
12#include "common/settings_enums.h"
12#include "yuzu/uisettings.h" 13#include "yuzu/uisettings.h"
13 14
14class QSettings; 15class QSettings;
@@ -51,7 +52,7 @@ public:
51 52
52 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map; 53 static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map;
53 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map; 54 static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map;
54 static const std::map<bool, QString> use_docked_mode_texts_map; 55 static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map;
55 static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map; 56 static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map;
56 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map; 57 static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
57 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map; 58 static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 8622dc184..fd6bebf0f 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -193,14 +193,10 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
193 : vsync_mode_combobox_enum_map[current_index]; 193 : vsync_mode_combobox_enum_map[current_index];
194 int index{}; 194 int index{};
195 const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device 195 const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
196 if (device == -1) {
197 // Invalid device
198 return;
199 }
200 196
201 const auto& present_modes = //< relevant vector of present modes for the selected device or API 197 const auto& present_modes = //< relevant vector of present modes for the selected device or API
202 backend == Settings::RendererBackend::Vulkan ? device_present_modes[device] 198 backend == Settings::RendererBackend::Vulkan && device > -1 ? device_present_modes[device]
203 : default_present_modes; 199 : default_present_modes;
204 200
205 vsync_mode_combobox->clear(); 201 vsync_mode_combobox->clear();
206 vsync_mode_combobox_enum_map.clear(); 202 vsync_mode_combobox_enum_map.clear();
@@ -497,11 +493,19 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
497} 493}
498 494
499Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 495Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
500 if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) { 496 const auto selected_backend = [&]() {
501 return Settings::values.renderer_backend.GetValue(true); 497 if (!Settings::IsConfiguringGlobal() && !api_restore_global_button->isEnabled()) {
498 return Settings::values.renderer_backend.GetValue(true);
499 }
500 return static_cast<Settings::RendererBackend>(
501 combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index())
502 .at(api_combobox->currentIndex())
503 .first);
504 }();
505
506 if (selected_backend == Settings::RendererBackend::Vulkan &&
507 UISettings::values.has_broken_vulkan) {
508 return Settings::RendererBackend::OpenGL;
502 } 509 }
503 return static_cast<Settings::RendererBackend>( 510 return selected_backend;
504 combobox_translations.at(Settings::EnumMetadata<Settings::RendererBackend>::Index())
505 .at(api_combobox->currentIndex())
506 .first);
507} 511}
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7fce85bca..e8f9ebfd8 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -4,6 +4,8 @@
4#include <memory> 4#include <memory>
5#include <thread> 5#include <thread>
6 6
7#include "common/settings.h"
8#include "common/settings_enums.h"
7#include "core/core.h" 9#include "core/core.h"
8#include "core/hid/emulated_controller.h" 10#include "core/hid/emulated_controller.h"
9#include "core/hid/hid_core.h" 11#include "core/hid/hid_core.h"
@@ -197,9 +199,11 @@ void ConfigureInput::ApplyConfiguration() {
197 199
198 advanced->ApplyConfiguration(); 200 advanced->ApplyConfiguration();
199 201
200 const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); 202 const bool pre_docked_mode = Settings::IsDockedMode();
201 Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked()); 203 const bool docked_mode_selected = ui->radioDocked->isChecked();
202 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue(), system); 204 Settings::values.use_docked_mode.SetValue(
205 docked_mode_selected ? Settings::ConsoleMode::Docked : Settings::ConsoleMode::Handheld);
206 OnDockedModeChanged(pre_docked_mode, docked_mode_selected, system);
203 207
204 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked()); 208 Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
205 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked()); 209 Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
@@ -267,8 +271,8 @@ void ConfigureInput::UpdateDockedState(bool is_handheld) {
267 ui->radioDocked->setEnabled(!is_handheld); 271 ui->radioDocked->setEnabled(!is_handheld);
268 ui->radioUndocked->setEnabled(!is_handheld); 272 ui->radioUndocked->setEnabled(!is_handheld);
269 273
270 ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue()); 274 ui->radioDocked->setChecked(Settings::IsDockedMode());
271 ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue()); 275 ui->radioUndocked->setChecked(!Settings::IsDockedMode());
272 276
273 // Also force into undocked mode if the controller type is handheld. 277 // Also force into undocked mode if the controller type is handheld.
274 if (is_handheld) { 278 if (is_handheld) {
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 4f9e8db08..b91d6ad4a 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -18,6 +18,7 @@
18 18
19#include "common/fs/fs_util.h" 19#include "common/fs/fs_util.h"
20#include "common/settings_enums.h" 20#include "common/settings_enums.h"
21#include "common/settings_input.h"
21#include "configuration/shared_widget.h" 22#include "configuration/shared_widget.h"
22#include "core/core.h" 23#include "core/core.h"
23#include "core/file_sys/control_metadata.h" 24#include "core/file_sys/control_metadata.h"
@@ -98,6 +99,12 @@ void ConfigurePerGame::ApplyConfiguration() {
98 addons_tab->ApplyConfiguration(); 99 addons_tab->ApplyConfiguration();
99 input_tab->ApplyConfiguration(); 100 input_tab->ApplyConfiguration();
100 101
102 if (Settings::IsDockedMode() && Settings::values.players.GetValue()[0].controller_type ==
103 Settings::ControllerType::Handheld) {
104 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
105 Settings::values.use_docked_mode.SetGlobal(true);
106 }
107
101 system.ApplySettings(); 108 system.ApplySettings();
102 Settings::LogSettings(); 109 Settings::LogSettings();
103 110
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index c4833f4e7..0c8e5c8b4 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -106,6 +106,11 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
106 push(Settings::values.linkage.by_category[Settings::Category::System]); 106 push(Settings::values.linkage.by_category[Settings::Category::System]);
107 107
108 for (auto setting : settings) { 108 for (auto setting : settings) {
109 if (setting->Id() == Settings::values.use_docked_mode.Id() &&
110 Settings::IsConfiguringGlobal()) {
111 continue;
112 }
113
109 ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs); 114 ConfigurationShared::Widget* widget = builder.BuildWidget(setting, apply_funcs);
110 115
111 if (widget == nullptr) { 116 if (widget == nullptr) {
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index 335810788..276bdbaba 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -135,7 +135,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
135 INSERT(Settings, region_index, "Region:", ""); 135 INSERT(Settings, region_index, "Region:", "");
136 INSERT(Settings, time_zone_index, "Time Zone:", ""); 136 INSERT(Settings, time_zone_index, "Time Zone:", "");
137 INSERT(Settings, sound_index, "Sound Output Mode:", ""); 137 INSERT(Settings, sound_index, "Sound Output Mode:", "");
138 INSERT(Settings, use_docked_mode, "", ""); 138 INSERT(Settings, use_docked_mode, "Console Mode:", "");
139 INSERT(Settings, current_user, "", ""); 139 INSERT(Settings, current_user, "", "");
140 140
141 // Controls 141 // Controls
@@ -379,6 +379,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
379 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), 379 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"),
380 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), 380 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"),
381 }}); 381 }});
382 translations->insert(
383 {Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
384 {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
382 385
383#undef PAIR 386#undef PAIR
384#undef CTX_PAIR 387#undef CTX_PAIR
diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp
index 4e45bc844..d63093985 100644
--- a/src/yuzu/configuration/shared_widget.cpp
+++ b/src/yuzu/configuration/shared_widget.cpp
@@ -23,6 +23,7 @@
23#include <QLineEdit> 23#include <QLineEdit>
24#include <QObject> 24#include <QObject>
25#include <QPushButton> 25#include <QPushButton>
26#include <QRadioButton>
26#include <QRegularExpression> 27#include <QRegularExpression>
27#include <QSizePolicy> 28#include <QSizePolicy>
28#include <QSlider> 29#include <QSlider>
@@ -171,6 +172,65 @@ QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
171 return combobox; 172 return combobox;
172} 173}
173 174
175QWidget* Widget::CreateRadioGroup(std::function<std::string()>& serializer,
176 std::function<void()>& restore_func,
177 const std::function<void()>& touch) {
178 const auto type = setting.EnumIndex();
179
180 QWidget* group = new QWidget(this);
181 QHBoxLayout* layout = new QHBoxLayout(group);
182 layout->setContentsMargins(0, 0, 0, 0);
183 group->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
184
185 const ComboboxTranslations* enumeration{nullptr};
186 if (combobox_enumerations.contains(type)) {
187 enumeration = &combobox_enumerations.at(type);
188 for (const auto& [id, name] : *enumeration) {
189 QRadioButton* radio_button = new QRadioButton(name, group);
190 layout->addWidget(radio_button);
191 radio_buttons.push_back({id, radio_button});
192 }
193 } else {
194 return group;
195 }
196
197 const auto get_selected = [=]() -> int {
198 for (const auto& [id, button] : radio_buttons) {
199 if (button->isChecked()) {
200 return id;
201 }
202 }
203 return -1;
204 };
205
206 const auto set_index = [=](u32 value) {
207 for (const auto& [id, button] : radio_buttons) {
208 button->setChecked(id == value);
209 }
210 };
211
212 const u32 setting_value = std::stoi(setting.ToString());
213 set_index(setting_value);
214
215 serializer = [get_selected]() {
216 int current = get_selected();
217 return std::to_string(current);
218 };
219
220 restore_func = [this, set_index]() {
221 const u32 global_value = std::stoi(RelevantDefault(setting));
222 set_index(global_value);
223 };
224
225 if (!Settings::IsConfiguringGlobal()) {
226 for (const auto& [id, button] : radio_buttons) {
227 QObject::connect(button, &QAbstractButton::clicked, [touch]() { touch(); });
228 }
229 }
230
231 return group;
232}
233
174QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer, 234QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
175 std::function<void()>& restore_func, 235 std::function<void()>& restore_func,
176 const std::function<void()>& touch, bool managed) { 236 const std::function<void()>& touch, bool managed) {
@@ -411,6 +471,8 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
411 return RequestType::Slider; 471 return RequestType::Slider;
412 case Settings::Specialization::Countable: 472 case Settings::Specialization::Countable:
413 return RequestType::SpinBox; 473 return RequestType::SpinBox;
474 case Settings::Specialization::Radio:
475 return RequestType::RadioGroup;
414 default: 476 default:
415 break; 477 break;
416 } 478 }
@@ -439,7 +501,11 @@ void Widget::SetupComponent(const QString& label, std::function<void()>& load_fu
439 if (setting.TypeId() == typeid(bool)) { 501 if (setting.TypeId() == typeid(bool)) {
440 data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch); 502 data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch);
441 } else if (setting.IsEnum()) { 503 } else if (setting.IsEnum()) {
442 data_component = CreateCombobox(serializer, restore_func, touch); 504 if (request == RequestType::RadioGroup) {
505 data_component = CreateRadioGroup(serializer, restore_func, touch);
506 } else {
507 data_component = CreateCombobox(serializer, restore_func, touch);
508 }
443 } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) || 509 } else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
444 type == typeid(s64) || type == typeid(u8)) { 510 type == typeid(s64) || type == typeid(u8)) {
445 switch (request) { 511 switch (request) {
diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h
index e64693bab..5303dd898 100644
--- a/src/yuzu/configuration/shared_widget.h
+++ b/src/yuzu/configuration/shared_widget.h
@@ -22,6 +22,7 @@ class QObject;
22class QPushButton; 22class QPushButton;
23class QSlider; 23class QSlider;
24class QSpinBox; 24class QSpinBox;
25class QRadioButton;
25 26
26namespace Settings { 27namespace Settings {
27class BasicSetting; 28class BasicSetting;
@@ -38,6 +39,7 @@ enum class RequestType {
38 LineEdit, 39 LineEdit,
39 HexEdit, 40 HexEdit,
40 DateTimeEdit, 41 DateTimeEdit,
42 RadioGroup,
41 MaxEnum, 43 MaxEnum,
42}; 44};
43 45
@@ -91,6 +93,7 @@ public:
91 QSlider* slider{}; 93 QSlider* slider{};
92 QComboBox* combobox{}; 94 QComboBox* combobox{};
93 QDateTimeEdit* date_time_edit{}; 95 QDateTimeEdit* date_time_edit{};
96 std::vector<std::pair<u32, QRadioButton*>> radio_buttons{};
94 97
95private: 98private:
96 void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed, 99 void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
@@ -106,6 +109,9 @@ private:
106 QWidget* CreateCombobox(std::function<std::string()>& serializer, 109 QWidget* CreateCombobox(std::function<std::string()>& serializer,
107 std::function<void()>& restore_func, 110 std::function<void()>& restore_func,
108 const std::function<void()>& touch); 111 const std::function<void()>& touch);
112 QWidget* CreateRadioGroup(std::function<std::string()>& serializer,
113 std::function<void()>& restore_func,
114 const std::function<void()>& touch);
109 QWidget* CreateLineEdit(std::function<std::string()>& serializer, 115 QWidget* CreateLineEdit(std::function<std::string()>& serializer,
110 std::function<void()>& restore_func, const std::function<void()>& touch, 116 std::function<void()>& restore_func, const std::function<void()>& touch,
111 bool managed = true); 117 bool managed = true);
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index b5a02700d..f254c1e1c 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -557,6 +557,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
557 QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); 557 QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
558 QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); 558 QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
559 QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); 559 QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
560 QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
560 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 561 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
561 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 562 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
562#ifndef WIN32 563#ifndef WIN32
@@ -588,10 +589,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
588 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); 589 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
589 }); 590 });
590 connect(start_game, &QAction::triggered, [this, path]() { 591 connect(start_game, &QAction::triggered, [this, path]() {
591 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal); 592 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal,
593 AmLaunchType::UserInitiated);
592 }); 594 });
593 connect(start_game_global, &QAction::triggered, [this, path]() { 595 connect(start_game_global, &QAction::triggered, [this, path]() {
594 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global); 596 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global,
597 AmLaunchType::UserInitiated);
595 }); 598 });
596 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { 599 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
597 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); 600 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
@@ -628,6 +631,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
628 connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() { 631 connect(dump_romfs_sdmc, &QAction::triggered, [this, program_id, path]() {
629 emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC); 632 emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::SDMC);
630 }); 633 });
634 connect(verify_integrity, &QAction::triggered,
635 [this, path]() { emit VerifyIntegrityRequested(path); });
631 connect(copy_tid, &QAction::triggered, 636 connect(copy_tid, &QAction::triggered,
632 [this, program_id]() { emit CopyTIDRequested(program_id); }); 637 [this, program_id]() { emit CopyTIDRequested(program_id); });
633 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { 638 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 6c2f75e53..1fcbbf0ba 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -28,6 +28,7 @@ class GameListWorker;
28class GameListSearchField; 28class GameListSearchField;
29class GameListDir; 29class GameListDir;
30class GMainWindow; 30class GMainWindow;
31enum class AmLaunchType;
31enum class StartGameType; 32enum class StartGameType;
32 33
33namespace FileSys { 34namespace FileSys {
@@ -103,7 +104,7 @@ public:
103 104
104signals: 105signals:
105 void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, 106 void BootGame(const QString& game_path, u64 program_id, std::size_t program_index,
106 StartGameType type); 107 StartGameType type, AmLaunchType launch_type);
107 void GameChosen(const QString& game_path, const u64 title_id = 0); 108 void GameChosen(const QString& game_path, const u64 title_id = 0);
108 void ShouldCancelWorker(); 109 void ShouldCancelWorker();
109 void OpenFolderRequested(u64 program_id, GameListOpenTarget target, 110 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
@@ -113,6 +114,7 @@ signals:
113 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, 114 void RemoveFileRequested(u64 program_id, GameListRemoveTarget target,
114 const std::string& game_path); 115 const std::string& game_path);
115 void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); 116 void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
117 void VerifyIntegrityRequested(const std::string& game_path);
116 void CopyTIDRequested(u64 program_id); 118 void CopyTIDRequested(u64 program_id);
117 void CreateShortcut(u64 program_id, const std::string& game_path, 119 void CreateShortcut(u64 program_id, const std::string& game_path,
118 GameListShortcutTarget target); 120 GameListShortcutTarget target);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 8e933af64..97d216638 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,6 +8,7 @@
8#include <iostream> 8#include <iostream>
9#include <memory> 9#include <memory>
10#include <thread> 10#include <thread>
11#include "core/loader/nca.h"
11#ifdef __APPLE__ 12#ifdef __APPLE__
12#include <unistd.h> // for chdir 13#include <unistd.h> // for chdir
13#endif 14#endif
@@ -442,8 +443,13 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
442 "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>" 443 "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>"
443 "here for instructions to fix the issue</a>.")); 444 "here for instructions to fix the issue</a>."));
444 445
446#ifdef HAS_OPENGL
445 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; 447 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
448#else
449 Settings::values.renderer_backend = Settings::RendererBackend::Null;
450#endif
446 451
452 UpdateAPIText();
447 renderer_status_button->setDisabled(true); 453 renderer_status_button->setDisabled(true);
448 renderer_status_button->setChecked(false); 454 renderer_status_button->setChecked(false);
449 } else { 455 } else {
@@ -1158,9 +1164,9 @@ void GMainWindow::InitializeWidgets() {
1158 [this](const QPoint& menu_location) { 1164 [this](const QPoint& menu_location) {
1159 QMenu context_menu; 1165 QMenu context_menu;
1160 1166
1161 for (auto const& docked_mode_pair : Config::use_docked_mode_texts_map) { 1167 for (auto const& pair : Config::use_docked_mode_texts_map) {
1162 context_menu.addAction(docked_mode_pair.second, [this, docked_mode_pair] { 1168 context_menu.addAction(pair.second, [this, &pair] {
1163 if (docked_mode_pair.first != Settings::values.use_docked_mode.GetValue()) { 1169 if (pair.first != Settings::values.use_docked_mode.GetValue()) {
1164 OnToggleDockedMode(); 1170 OnToggleDockedMode();
1165 } 1171 }
1166 }); 1172 });
@@ -1447,6 +1453,8 @@ void GMainWindow::ConnectWidgetEvents() {
1447 &GMainWindow::OnGameListRemoveInstalledEntry); 1453 &GMainWindow::OnGameListRemoveInstalledEntry);
1448 connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); 1454 connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile);
1449 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); 1455 connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
1456 connect(game_list, &GameList::VerifyIntegrityRequested, this,
1457 &GMainWindow::OnGameListVerifyIntegrity);
1450 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); 1458 connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
1451 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, 1459 connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
1452 &GMainWindow::OnGameListNavigateToGamedbEntry); 1460 &GMainWindow::OnGameListNavigateToGamedbEntry);
@@ -1547,6 +1555,7 @@ void GMainWindow::ConnectMenuEvents() {
1547 1555
1548 // Help 1556 // Help
1549 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder); 1557 connect_menu(ui->action_Open_yuzu_Folder, &GMainWindow::OnOpenYuzuFolder);
1558 connect_menu(ui->action_Verify_installed_contents, &GMainWindow::OnVerifyInstalledContents);
1550 connect_menu(ui->action_About, &GMainWindow::OnAbout); 1559 connect_menu(ui->action_About, &GMainWindow::OnAbout);
1551} 1560}
1552 1561
@@ -1698,7 +1707,8 @@ void GMainWindow::AllowOSSleep() {
1698#endif 1707#endif
1699} 1708}
1700 1709
1701bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) { 1710bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index,
1711 AmLaunchType launch_type) {
1702 // Shutdown previous session if the emu thread is still active... 1712 // Shutdown previous session if the emu thread is still active...
1703 if (emu_thread != nullptr) { 1713 if (emu_thread != nullptr) {
1704 ShutdownGame(); 1714 ShutdownGame();
@@ -1710,6 +1720,10 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1710 1720
1711 system->SetFilesystem(vfs); 1721 system->SetFilesystem(vfs);
1712 1722
1723 if (launch_type == AmLaunchType::UserInitiated) {
1724 system->GetUserChannel().clear();
1725 }
1726
1713 system->SetAppletFrontendSet({ 1727 system->SetAppletFrontendSet({
1714 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings 1728 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
1715 (UISettings::values.controller_applet_disabled.GetValue() == true) 1729 (UISettings::values.controller_applet_disabled.GetValue() == true)
@@ -1849,7 +1863,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) {
1849} 1863}
1850 1864
1851void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, 1865void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
1852 StartGameType type) { 1866 StartGameType type, AmLaunchType launch_type) {
1853 LOG_INFO(Frontend, "yuzu starting..."); 1867 LOG_INFO(Frontend, "yuzu starting...");
1854 StoreRecentFile(filename); // Put the filename on top of the list 1868 StoreRecentFile(filename); // Put the filename on top of the list
1855 1869
@@ -1893,7 +1907,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1893 } 1907 }
1894 } 1908 }
1895 1909
1896 if (!LoadROM(filename, program_id, program_index)) { 1910 if (!LoadROM(filename, program_id, program_index, launch_type)) {
1897 return; 1911 return;
1898 } 1912 }
1899 1913
@@ -2010,8 +2024,16 @@ bool GMainWindow::OnShutdownBegin() {
2010 2024
2011 emit EmulationStopping(); 2025 emit EmulationStopping();
2012 2026
2027 int shutdown_time = 1000;
2028
2029 if (system->DebuggerEnabled()) {
2030 shutdown_time = 0;
2031 } else if (system->GetExitLocked()) {
2032 shutdown_time = 5000;
2033 }
2034
2013 shutdown_timer.setSingleShot(true); 2035 shutdown_timer.setSingleShot(true);
2014 shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); 2036 shutdown_timer.start(shutdown_time);
2015 connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); 2037 connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired);
2016 connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); 2038 connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped);
2017 2039
@@ -2267,40 +2289,62 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
2267 QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); 2289 QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
2268} 2290}
2269 2291
2270static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { 2292static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog,
2271 std::size_t out = 0; 2293 const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest,
2272 2294 bool full) {
2273 for (const auto& subdir : dir->GetSubdirectories()) {
2274 out += 1 + CalculateRomFSEntrySize(subdir, full);
2275 }
2276
2277 return out + (full ? dir->GetFiles().size() : 0);
2278}
2279
2280static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src,
2281 const FileSys::VirtualDir& dest, std::size_t block_size, bool full) {
2282 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) 2295 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
2283 return false; 2296 return false;
2284 if (dialog.wasCanceled()) 2297 if (dialog.wasCanceled())
2285 return false; 2298 return false;
2286 2299
2300 std::vector<u8> buffer(CopyBufferSize);
2301 auto last_timestamp = std::chrono::steady_clock::now();
2302
2303 const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file,
2304 const FileSys::VirtualFile& dest_file) {
2305 if (src_file == nullptr || dest_file == nullptr) {
2306 return false;
2307 }
2308 if (!dest_file->Resize(src_file->GetSize())) {
2309 return false;
2310 }
2311
2312 for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) {
2313 if (dialog.wasCanceled()) {
2314 dest_file->Resize(0);
2315 return false;
2316 }
2317
2318 using namespace std::literals::chrono_literals;
2319 const auto new_timestamp = std::chrono::steady_clock::now();
2320
2321 if ((new_timestamp - last_timestamp) > 33ms) {
2322 last_timestamp = new_timestamp;
2323 dialog.setValue(
2324 static_cast<int>(std::min(read_size, total_size) * 100 / total_size));
2325 QCoreApplication::processEvents();
2326 }
2327
2328 const auto read = src_file->Read(buffer.data(), buffer.size(), i);
2329 dest_file->Write(buffer.data(), read, i);
2330
2331 read_size += read;
2332 }
2333
2334 return true;
2335 };
2336
2287 if (full) { 2337 if (full) {
2288 for (const auto& file : src->GetFiles()) { 2338 for (const auto& file : src->GetFiles()) {
2289 const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); 2339 const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
2290 if (!FileSys::VfsRawCopy(file, out, block_size)) 2340 if (!QtRawCopy(file, out))
2291 return false;
2292 dialog.setValue(dialog.value() + 1);
2293 if (dialog.wasCanceled())
2294 return false; 2341 return false;
2295 } 2342 }
2296 } 2343 }
2297 2344
2298 for (const auto& dir : src->GetSubdirectories()) { 2345 for (const auto& dir : src->GetSubdirectories()) {
2299 const auto out = dest->CreateSubdirectory(dir->GetName()); 2346 const auto out = dest->CreateSubdirectory(dir->GetName());
2300 if (!RomFSRawCopy(dialog, dir, out, block_size, full)) 2347 if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full))
2301 return false;
2302 dialog.setValue(dialog.value() + 1);
2303 if (dialog.wasCanceled())
2304 return false; 2348 return false;
2305 } 2349 }
2306 2350
@@ -2573,50 +2617,48 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2573 return; 2617 return;
2574 } 2618 }
2575 2619
2576 FileSys::VirtualFile base_romfs; 2620 FileSys::VirtualFile packed_update_raw{};
2577 if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) { 2621 loader->ReadUpdateRaw(packed_update_raw);
2578 failed();
2579 return;
2580 }
2581 2622
2582 const auto& installed = system->GetContentProvider(); 2623 const auto& installed = system->GetContentProvider();
2583 const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
2584 2624
2585 if (!romfs_title_id) { 2625 u64 title_id{};
2626 u8 raw_type{};
2627 if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) {
2586 failed(); 2628 failed();
2587 return; 2629 return;
2588 } 2630 }
2589 2631
2590 const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program 2632 const auto type = static_cast<FileSys::ContentRecordType>(raw_type);
2591 : FileSys::ContentRecordType::Data; 2633 const auto base_nca = installed.GetEntry(title_id, type);
2592 const auto base_nca = installed.GetEntry(*romfs_title_id, type);
2593 if (!base_nca) { 2634 if (!base_nca) {
2594 failed(); 2635 failed();
2595 return; 2636 return;
2596 } 2637 }
2597 2638
2639 const FileSys::NCA update_nca{packed_update_raw, nullptr};
2640 if (type != FileSys::ContentRecordType::Program ||
2641 update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS ||
2642 update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) {
2643 packed_update_raw = {};
2644 }
2645
2646 const auto base_romfs = base_nca->GetRomFS();
2647 if (!base_romfs) {
2648 failed();
2649 return;
2650 }
2651
2598 const auto dump_dir = 2652 const auto dump_dir =
2599 target == DumpRomFSTarget::Normal 2653 target == DumpRomFSTarget::Normal
2600 ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) 2654 ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)
2601 : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; 2655 : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents";
2602 const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); 2656 const auto romfs_dir = fmt::format("{:016X}/romfs", title_id);
2603 2657
2604 const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); 2658 const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir);
2605 2659
2606 FileSys::VirtualFile romfs; 2660 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed};
2607 2661 auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false);
2608 if (*romfs_title_id == program_id) {
2609 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), installed};
2610 romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, nullptr, false);
2611 } else {
2612 romfs = installed.GetEntry(*romfs_title_id, type)->GetRomFS();
2613 }
2614
2615 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
2616 if (extracted == nullptr) {
2617 failed();
2618 return;
2619 }
2620 2662
2621 const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); 2663 const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite);
2622 2664
@@ -2640,11 +2682,16 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2640 return; 2682 return;
2641 } 2683 }
2642 2684
2685 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
2686 if (extracted == nullptr) {
2687 failed();
2688 return;
2689 }
2690
2643 const auto full = res == selections.constFirst(); 2691 const auto full = res == selections.constFirst();
2644 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
2645 2692
2646 // The minimum required space is the size of the extracted RomFS + 1 GiB 2693 // The expected required space is the size of the RomFS + 1 GiB
2647 const auto minimum_free_space = extracted->GetSize() + 0x40000000; 2694 const auto minimum_free_space = romfs->GetSize() + 0x40000000;
2648 2695
2649 if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) { 2696 if (full && Common::FS::GetFreeSpaceSize(path) < minimum_free_space) {
2650 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 2697 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
@@ -2655,12 +2702,15 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2655 return; 2702 return;
2656 } 2703 }
2657 2704
2658 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 2705 QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, 100, this);
2659 static_cast<s32>(entry_size), this);
2660 progress.setWindowModality(Qt::WindowModal); 2706 progress.setWindowModality(Qt::WindowModal);
2661 progress.setMinimumDuration(100); 2707 progress.setMinimumDuration(100);
2708 progress.setAutoClose(false);
2709 progress.setAutoReset(false);
2710
2711 size_t read_size = 0;
2662 2712
2663 if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) { 2713 if (RomFSRawCopy(romfs->GetSize(), read_size, progress, extracted, out, full)) {
2664 progress.close(); 2714 progress.close();
2665 QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), 2715 QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
2666 tr("The operation completed successfully.")); 2716 tr("The operation completed successfully."));
@@ -2672,6 +2722,54 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
2672 } 2722 }
2673} 2723}
2674 2724
2725void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
2726 const auto NotImplemented = [this] {
2727 QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
2728 tr("File contents were not checked for validity."));
2729 };
2730 const auto Failed = [this] {
2731 QMessageBox::critical(this, tr("Integrity verification failed!"),
2732 tr("File contents may be corrupt."));
2733 };
2734
2735 const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
2736 if (loader == nullptr) {
2737 NotImplemented();
2738 return;
2739 }
2740
2741 QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
2742 progress.setWindowModality(Qt::WindowModal);
2743 progress.setMinimumDuration(100);
2744 progress.setAutoClose(false);
2745 progress.setAutoReset(false);
2746
2747 const auto QtProgressCallback = [&](size_t processed_size, size_t total_size) {
2748 if (progress.wasCanceled()) {
2749 return false;
2750 }
2751
2752 progress.setValue(static_cast<int>((processed_size * 100) / total_size));
2753 return true;
2754 };
2755
2756 const auto status = loader->VerifyIntegrity(QtProgressCallback);
2757 if (progress.wasCanceled() ||
2758 status == Loader::ResultStatus::ErrorIntegrityVerificationNotImplemented) {
2759 NotImplemented();
2760 return;
2761 }
2762
2763 if (status == Loader::ResultStatus::ErrorIntegrityVerificationFailed) {
2764 Failed();
2765 return;
2766 }
2767
2768 progress.close();
2769 QMessageBox::information(this, tr("Integrity verification succeeded!"),
2770 tr("The operation completed successfully."));
2771}
2772
2675void GMainWindow::OnGameListCopyTID(u64 program_id) { 2773void GMainWindow::OnGameListCopyTID(u64 program_id) {
2676 QClipboard* clipboard = QGuiApplication::clipboard(); 2774 QClipboard* clipboard = QGuiApplication::clipboard();
2677 clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id))); 2775 clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id)));
@@ -3261,7 +3359,7 @@ void GMainWindow::OnPauseContinueGame() {
3261} 3359}
3262 3360
3263void GMainWindow::OnStopGame() { 3361void GMainWindow::OnStopGame() {
3264 if (system->GetExitLock() && !ConfirmForceLockedExit()) { 3362 if (system->GetExitLocked() && !ConfirmForceLockedExit()) {
3265 return; 3363 return;
3266 } 3364 }
3267 3365
@@ -3278,7 +3376,8 @@ void GMainWindow::OnLoadComplete() {
3278 3376
3279void GMainWindow::OnExecuteProgram(std::size_t program_index) { 3377void GMainWindow::OnExecuteProgram(std::size_t program_index) {
3280 ShutdownGame(); 3378 ShutdownGame();
3281 BootGame(last_filename_booted, 0, program_index); 3379 BootGame(last_filename_booted, 0, program_index, StartGameType::Normal,
3380 AmLaunchType::ApplicationInitiated);
3282} 3381}
3283 3382
3284void GMainWindow::OnExit() { 3383void GMainWindow::OnExit() {
@@ -3674,7 +3773,7 @@ void GMainWindow::OnTasReset() {
3674} 3773}
3675 3774
3676void GMainWindow::OnToggleDockedMode() { 3775void GMainWindow::OnToggleDockedMode() {
3677 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 3776 const bool is_docked = Settings::IsDockedMode();
3678 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 3777 auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
3679 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); 3778 auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
3680 3779
@@ -3688,7 +3787,8 @@ void GMainWindow::OnToggleDockedMode() {
3688 controller_dialog->refreshConfiguration(); 3787 controller_dialog->refreshConfiguration();
3689 } 3788 }
3690 3789
3691 Settings::values.use_docked_mode.SetValue(!is_docked); 3790 Settings::values.use_docked_mode.SetValue(is_docked ? Settings::ConsoleMode::Handheld
3791 : Settings::ConsoleMode::Docked);
3692 UpdateDockedButton(); 3792 UpdateDockedButton();
3693 OnDockedModeChanged(is_docked, !is_docked, *system); 3793 OnDockedModeChanged(is_docked, !is_docked, *system);
3694} 3794}
@@ -3757,10 +3857,14 @@ void GMainWindow::OnToggleAdaptingFilter() {
3757 3857
3758void GMainWindow::OnToggleGraphicsAPI() { 3858void GMainWindow::OnToggleGraphicsAPI() {
3759 auto api = Settings::values.renderer_backend.GetValue(); 3859 auto api = Settings::values.renderer_backend.GetValue();
3760 if (api == Settings::RendererBackend::OpenGL) { 3860 if (api != Settings::RendererBackend::Vulkan) {
3761 api = Settings::RendererBackend::Vulkan; 3861 api = Settings::RendererBackend::Vulkan;
3762 } else { 3862 } else {
3863#ifdef HAS_OPENGL
3763 api = Settings::RendererBackend::OpenGL; 3864 api = Settings::RendererBackend::OpenGL;
3865#else
3866 api = Settings::RendererBackend::Null;
3867#endif
3764 } 3868 }
3765 Settings::values.renderer_backend.SetValue(api); 3869 Settings::values.renderer_backend.SetValue(api);
3766 renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan); 3870 renderer_status_button->setChecked(api == Settings::RendererBackend::Vulkan);
@@ -3904,6 +4008,108 @@ void GMainWindow::OnOpenYuzuFolder() {
3904 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir)))); 4008 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir))));
3905} 4009}
3906 4010
4011void GMainWindow::OnVerifyInstalledContents() {
4012 // Declare sizes.
4013 size_t total_size = 0;
4014 size_t processed_size = 0;
4015
4016 // Initialize a progress dialog.
4017 QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
4018 progress.setWindowModality(Qt::WindowModal);
4019 progress.setMinimumDuration(100);
4020 progress.setAutoClose(false);
4021 progress.setAutoReset(false);
4022
4023 // Declare a list of file names which failed to verify.
4024 std::vector<std::string> failed;
4025
4026 // Declare progress callback.
4027 auto QtProgressCallback = [&](size_t nca_processed, size_t nca_total) {
4028 if (progress.wasCanceled()) {
4029 return false;
4030 }
4031 progress.setValue(static_cast<int>(((processed_size + nca_processed) * 100) / total_size));
4032 return true;
4033 };
4034
4035 // Get content registries.
4036 auto bis_contents = system->GetFileSystemController().GetSystemNANDContents();
4037 auto user_contents = system->GetFileSystemController().GetUserNANDContents();
4038
4039 std::vector<FileSys::RegisteredCache*> content_providers;
4040 if (bis_contents) {
4041 content_providers.push_back(bis_contents);
4042 }
4043 if (user_contents) {
4044 content_providers.push_back(user_contents);
4045 }
4046
4047 // Get associated NCA files.
4048 std::vector<FileSys::VirtualFile> nca_files;
4049
4050 // Get all installed IDs.
4051 for (auto nca_provider : content_providers) {
4052 const auto entries = nca_provider->ListEntriesFilter();
4053
4054 for (const auto& entry : entries) {
4055 auto nca_file = nca_provider->GetEntryRaw(entry.title_id, entry.type);
4056 if (!nca_file) {
4057 continue;
4058 }
4059
4060 total_size += nca_file->GetSize();
4061 nca_files.push_back(std::move(nca_file));
4062 }
4063 }
4064
4065 // Using the NCA loader, determine if all NCAs are valid.
4066 for (auto& nca_file : nca_files) {
4067 Loader::AppLoader_NCA nca_loader(nca_file);
4068
4069 auto status = nca_loader.VerifyIntegrity(QtProgressCallback);
4070 if (progress.wasCanceled()) {
4071 break;
4072 }
4073 if (status != Loader::ResultStatus::Success) {
4074 FileSys::NCA nca(nca_file);
4075 const auto title_id = nca.GetTitleId();
4076 std::string title_name = "unknown";
4077
4078 const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
4079 FileSys::ContentRecordType::Control);
4080 if (control && control->GetStatus() == Loader::ResultStatus::Success) {
4081 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
4082 *provider};
4083 const auto [nacp, logo] = pm.ParseControlNCA(*control);
4084 if (nacp) {
4085 title_name = nacp->GetApplicationName();
4086 }
4087 }
4088
4089 if (title_id > 0) {
4090 failed.push_back(
4091 fmt::format("{} ({:016X}) ({})", nca_file->GetName(), title_id, title_name));
4092 } else {
4093 failed.push_back(fmt::format("{} (unknown)", nca_file->GetName()));
4094 }
4095 }
4096
4097 processed_size += nca_file->GetSize();
4098 }
4099
4100 progress.close();
4101
4102 if (failed.size() > 0) {
4103 auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(failed, "\n")));
4104 QMessageBox::critical(
4105 this, tr("Integrity verification failed!"),
4106 tr("Verification failed for the following files:\n\n%1").arg(failed_names));
4107 } else {
4108 QMessageBox::information(this, tr("Integrity verification succeeded!"),
4109 tr("The operation completed successfully."));
4110 }
4111}
4112
3907void GMainWindow::OnAbout() { 4113void GMainWindow::OnAbout() {
3908 AboutDialog aboutDialog(this); 4114 AboutDialog aboutDialog(this);
3909 aboutDialog.exec(); 4115 aboutDialog.exec();
@@ -4118,10 +4324,10 @@ void GMainWindow::UpdateGPUAccuracyButton() {
4118} 4324}
4119 4325
4120void GMainWindow::UpdateDockedButton() { 4326void GMainWindow::UpdateDockedButton() {
4121 const bool is_docked = Settings::values.use_docked_mode.GetValue(); 4327 const auto console_mode = Settings::values.use_docked_mode.GetValue();
4122 dock_status_button->setChecked(is_docked); 4328 dock_status_button->setChecked(Settings::IsDockedMode());
4123 dock_status_button->setText( 4329 dock_status_button->setText(
4124 Config::use_docked_mode_texts_map.find(is_docked)->second.toUpper()); 4330 Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
4125} 4331}
4126 4332
4127void GMainWindow::UpdateAPIText() { 4333void GMainWindow::UpdateAPIText() {
@@ -4349,28 +4555,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
4349 return mii_nca->GetRomFS().get() != nullptr; 4555 return mii_nca->GetRomFS().get() != nullptr;
4350} 4556}
4351 4557
4352std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, 4558bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
4353 u64 program_id) { 4559 u64* selected_title_id, u8* selected_content_record_type) {
4354 const auto dlc_entries = 4560 using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>;
4355 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 4561 boost::container::flat_map<u64, ContentInfo> available_title_ids;
4356 std::vector<FileSys::ContentProviderEntry> dlc_match; 4562
4357 dlc_match.reserve(dlc_entries.size()); 4563 const auto RetrieveEntries = [&](FileSys::TitleType title_type,
4358 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), 4564 FileSys::ContentRecordType record_type) {
4359 [&program_id, &installed](const FileSys::ContentProviderEntry& entry) { 4565 const auto entries = installed.ListEntriesFilter(title_type, record_type);
4360 return FileSys::GetBaseTitleID(entry.title_id) == program_id && 4566 for (const auto& entry : entries) {
4361 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; 4567 if (FileSys::GetBaseTitleID(entry.title_id) == program_id &&
4362 }); 4568 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) {
4363 4569 available_title_ids[entry.title_id] = {title_type, record_type};
4364 std::vector<u64> romfs_tids; 4570 }
4365 romfs_tids.push_back(program_id); 4571 }
4366 for (const auto& entry : dlc_match) { 4572 };
4367 romfs_tids.push_back(entry.title_id); 4573
4368 } 4574 RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program);
4369 4575 RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
4370 if (romfs_tids.size() > 1) { 4576
4371 QStringList list{QStringLiteral("Base")}; 4577 if (available_title_ids.empty()) {
4372 for (std::size_t i = 1; i < romfs_tids.size(); ++i) { 4578 return false;
4373 list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); 4579 }
4580
4581 size_t title_index = 0;
4582
4583 if (available_title_ids.size() > 1) {
4584 QStringList list;
4585 for (auto& [title_id, content_info] : available_title_ids) {
4586 const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id));
4587 if (content_info.first == FileSys::TitleType::Application) {
4588 list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id));
4589 } else {
4590 list.push_back(
4591 QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id));
4592 }
4374 } 4593 }
4375 4594
4376 bool ok; 4595 bool ok;
@@ -4378,13 +4597,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv
4378 this, tr("Select RomFS Dump Target"), 4597 this, tr("Select RomFS Dump Target"),
4379 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); 4598 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
4380 if (!ok) { 4599 if (!ok) {
4381 return {}; 4600 return false;
4382 } 4601 }
4383 4602
4384 return romfs_tids[list.indexOf(res)]; 4603 title_index = list.indexOf(res);
4385 } 4604 }
4386 4605
4387 return program_id; 4606 const auto selected_info = available_title_ids.nth(title_index);
4607 *selected_title_id = selected_info->first;
4608 *selected_content_record_type = static_cast<u8>(selected_info->second.second);
4609 return true;
4388} 4610}
4389 4611
4390bool GMainWindow::ConfirmClose() { 4612bool GMainWindow::ConfirmClose() {
@@ -4514,6 +4736,8 @@ void GMainWindow::RequestGameExit() {
4514 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); 4736 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
4515 bool has_signalled = false; 4737 bool has_signalled = false;
4516 4738
4739 system->SetExitRequested(true);
4740
4517 if (applet_oe != nullptr) { 4741 if (applet_oe != nullptr) {
4518 applet_oe->GetMessageQueue()->RequestExit(); 4742 applet_oe->GetMessageQueue()->RequestExit();
4519 has_signalled = true; 4743 has_signalled = true;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 1b7055122..cf191f698 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -58,6 +58,11 @@ enum class StartGameType {
58 Global, // Only uses global configuration 58 Global, // Only uses global configuration
59}; 59};
60 60
61enum class AmLaunchType {
62 UserInitiated,
63 ApplicationInitiated,
64};
65
61namespace Core { 66namespace Core {
62enum class SystemResultStatus : u32; 67enum class SystemResultStatus : u32;
63class System; 68class System;
@@ -239,9 +244,11 @@ private:
239 void PreventOSSleep(); 244 void PreventOSSleep();
240 void AllowOSSleep(); 245 void AllowOSSleep();
241 246
242 bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index); 247 bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index,
248 AmLaunchType launch_type);
243 void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0, 249 void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0,
244 StartGameType with_config = StartGameType::Normal); 250 StartGameType with_config = StartGameType::Normal,
251 AmLaunchType launch_type = AmLaunchType::UserInitiated);
245 void ShutdownGame(); 252 void ShutdownGame();
246 253
247 void ShowTelemetryCallout(); 254 void ShowTelemetryCallout();
@@ -313,6 +320,7 @@ private slots:
313 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, 320 void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target,
314 const std::string& game_path); 321 const std::string& game_path);
315 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); 322 void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target);
323 void OnGameListVerifyIntegrity(const std::string& game_path);
316 void OnGameListCopyTID(u64 program_id); 324 void OnGameListCopyTID(u64 program_id);
317 void OnGameListNavigateToGamedbEntry(u64 program_id, 325 void OnGameListNavigateToGamedbEntry(u64 program_id,
318 const CompatibilityList& compatibility_list); 326 const CompatibilityList& compatibility_list);
@@ -342,6 +350,7 @@ private slots:
342 void OnConfigurePerGame(); 350 void OnConfigurePerGame();
343 void OnLoadAmiibo(); 351 void OnLoadAmiibo();
344 void OnOpenYuzuFolder(); 352 void OnOpenYuzuFolder();
353 void OnVerifyInstalledContents();
345 void OnAbout(); 354 void OnAbout();
346 void OnToggleFilterBar(); 355 void OnToggleFilterBar();
347 void OnToggleStatusBar(); 356 void OnToggleStatusBar();
@@ -375,7 +384,8 @@ private:
375 void RemoveAllTransferableShaderCaches(u64 program_id); 384 void RemoveAllTransferableShaderCaches(u64 program_id);
376 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); 385 void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
377 void RemoveCacheStorage(u64 program_id); 386 void RemoveCacheStorage(u64 program_id);
378 std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); 387 bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
388 u64* selected_title_id, u8* selected_content_record_type);
379 InstallResult InstallNSPXCI(const QString& filename); 389 InstallResult InstallNSPXCI(const QString& filename);
380 InstallResult InstallNCA(const QString& filename); 390 InstallResult InstallNCA(const QString& filename);
381 void MigrateConfigFiles(); 391 void MigrateConfigFiles();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 013ba0ceb..e54d7d75d 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -148,6 +148,7 @@
148 <addaction name="action_Configure_Tas"/> 148 <addaction name="action_Configure_Tas"/>
149 </widget> 149 </widget>
150 <addaction name="action_Rederive"/> 150 <addaction name="action_Rederive"/>
151 <addaction name="action_Verify_installed_contents"/>
151 <addaction name="separator"/> 152 <addaction name="separator"/>
152 <addaction name="action_Capture_Screenshot"/> 153 <addaction name="action_Capture_Screenshot"/>
153 <addaction name="menuTAS"/> 154 <addaction name="menuTAS"/>
@@ -214,6 +215,11 @@
214 <string>&amp;Reinitialize keys...</string> 215 <string>&amp;Reinitialize keys...</string>
215 </property> 216 </property>
216 </action> 217 </action>
218 <action name="action_Verify_installed_contents">
219 <property name="text">
220 <string>Verify installed contents</string>
221 </property>
222 </action>
217 <action name="action_About"> 223 <action name="action_About">
218 <property name="text"> 224 <property name="text">
219 <string>&amp;About yuzu</string> 225 <string>&amp;About yuzu</string>
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index d0433ffc6..55d0938f7 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -265,7 +265,7 @@ int main(int argc, char** argv) {
265 password = match[2]; 265 password = match[2];
266 address = match[3]; 266 address = match[3];
267 if (!match[4].str().empty()) 267 if (!match[4].str().empty())
268 port = std::stoi(match[4]); 268 port = static_cast<u16>(std::stoi(match[4]));
269 std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); 269 std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
270 if (!std::regex_match(nickname, nickname_re)) { 270 if (!std::regex_match(nickname, nickname_re)) {
271 std::cout 271 std::cout
@@ -358,6 +358,7 @@ int main(int argc, char** argv) {
358 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 358 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
359 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 359 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
360 system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); 360 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
361 system.GetUserChannel().clear();
361 362
362 const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)}; 363 const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)};
363 364