summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_renderer.cpp89
-rw-r--r--src/audio_core/audio_renderer.h49
-rw-r--r--src/audio_core/cubeb_sink.cpp3
-rw-r--r--src/audio_core/time_stretch.cpp9
-rw-r--r--src/audio_core/time_stretch.h1
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/alignment.h12
-rw-r--r--src/common/file_util.cpp13
-rw-r--r--src/common/hex_util.cpp19
-rw-r--r--src/common/hex_util.h5
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/log.h3
-rw-r--r--src/common/memory_util.cpp177
-rw-r--r--src/common/memory_util.h21
-rw-r--r--src/common/param_package.cpp18
-rw-r--r--src/common/param_package.h2
-rw-r--r--src/common/telemetry.h4
-rw-r--r--src/common/web_result.h1
-rw-r--r--src/core/CMakeLists.txt31
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp9
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h4
-rw-r--r--src/core/core.cpp83
-rw-r--r--src/core/core.h49
-rw-r--r--src/core/core_cpu.cpp14
-rw-r--r--src/core/core_cpu.h17
-rw-r--r--src/core/crypto/key_manager.cpp839
-rw-r--r--src/core/crypto/key_manager.h109
-rw-r--r--src/core/crypto/partition_data_manager.cpp594
-rw-r--r--src/core/crypto/partition_data_manager.h109
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h8
-rw-r--r--src/core/file_sys/card_image.cpp14
-rw-r--r--src/core/file_sys/card_image.h5
-rw-r--r--src/core/file_sys/content_archive.cpp561
-rw-r--r--src/core/file_sys/content_archive.h29
-rw-r--r--src/core/file_sys/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp26
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h7
-rw-r--r--src/core/file_sys/ips_layer.cpp276
-rw-r--r--src/core/file_sys/ips_layer.h29
-rw-r--r--src/core/file_sys/partition_filesystem.cpp16
-rw-r--r--src/core/file_sys/partition_filesystem.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp158
-rw-r--r--src/core/file_sys/patch_manager.h12
-rw-r--r--src/core/file_sys/registered_cache.cpp91
-rw-r--r--src/core/file_sys/registered_cache.h43
-rw-r--r--src/core/file_sys/romfs.cpp4
-rw-r--r--src/core/file_sys/romfs.h2
-rw-r--r--src/core/file_sys/romfs_factory.cpp7
-rw-r--r--src/core/file_sys/romfs_factory.h2
-rw-r--r--src/core/file_sys/savedata_factory.cpp13
-rw-r--r--src/core/file_sys/sdmc_factory.cpp8
-rw-r--r--src/core/file_sys/sdmc_factory.h4
-rw-r--r--src/core/file_sys/submission_package.cpp9
-rw-r--r--src/core/file_sys/submission_package.h3
-rw-r--r--src/core/file_sys/vfs.cpp12
-rw-r--r--src/core/file_sys/vfs.h46
-rw-r--r--src/core/file_sys/vfs_layered.cpp3
-rw-r--r--src/core/file_sys/vfs_layered.h3
-rw-r--r--src/core/file_sys/vfs_offset.cpp4
-rw-r--r--src/core/file_sys/vfs_offset.h2
-rw-r--r--src/core/file_sys/vfs_real.cpp3
-rw-r--r--src/core/file_sys/vfs_real.h3
-rw-r--r--src/core/file_sys/vfs_static.h4
-rw-r--r--src/core/file_sys/vfs_types.h21
-rw-r--r--src/core/file_sys/vfs_vector.cpp7
-rw-r--r--src/core/file_sys/vfs_vector.h3
-rw-r--r--src/core/file_sys/xts_archive.cpp3
-rw-r--r--src/core/file_sys/xts_archive.h3
-rw-r--r--src/core/gdbstub/gdbstub.cpp6
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/ipc_helpers.h3
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp2
-rw-r--r--src/core/hle/kernel/errors.h23
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp25
-rw-r--r--src/core/hle/kernel/hle_ipc.h21
-rw-r--r--src/core/hle/kernel/kernel.cpp26
-rw-r--r--src/core/hle/kernel/kernel.h16
-rw-r--r--src/core/hle/kernel/mutex.cpp2
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp40
-rw-r--r--src/core/hle/kernel/process.h76
-rw-r--r--src/core/hle/kernel/scheduler.cpp32
-rw-r--r--src/core/hle/kernel/scheduler.h19
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_session.cpp7
-rw-r--r--src/core/hle/kernel/shared_memory.cpp7
-rw-r--r--src/core/hle/kernel/svc.cpp448
-rw-r--r--src/core/hle/kernel/svc.h31
-rw-r--r--src/core/hle/kernel/svc_wrap.h64
-rw-r--r--src/core/hle/kernel/thread.cpp39
-rw-r--r--src/core/hle/kernel/thread.h19
-rw-r--r--src/core/hle/kernel/vm_manager.cpp75
-rw-r--r--src/core/hle/kernel/vm_manager.h23
-rw-r--r--src/core/hle/service/acc/acc.cpp76
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp204
-rw-r--r--src/core/hle/service/acc/profile_manager.h38
-rw-r--r--src/core/hle/service/am/am.cpp125
-rw-r--r--src/core/hle/service/am/am.h4
-rw-r--r--src/core/hle/service/am/applet_ae.cpp4
-rw-r--r--src/core/hle/service/am/applet_oe.cpp3
-rw-r--r--src/core/hle/service/am/idle.cpp6
-rw-r--r--src/core/hle/service/am/omm.cpp34
-rw-r--r--src/core/hle/service/am/tcap.cpp23
-rw-r--r--src/core/hle/service/am/tcap.h17
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp47
-rw-r--r--src/core/hle/service/aoc/aoc_u.h2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp37
-rw-r--r--src/core/hle/service/audio/hwopus.cpp39
-rw-r--r--src/core/hle/service/es/es.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp38
-rw-r--r--src/core/hle/service/filesystem/filesystem.h13
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp34
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp30
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h45
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp42
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h56
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h63
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h50
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h50
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp620
-rw-r--r--src/core/hle/service/hid/controllers/npad.h289
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h34
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp65
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h63
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp45
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h60
-rw-r--r--src/core/hle/service/hid/hid.cpp518
-rw-r--r--src/core/hle/service/hid/hid.h402
-rw-r--r--src/core/hle/service/lbl/lbl.cpp56
-rw-r--r--src/core/hle/service/ldr/ldr.cpp52
-rw-r--r--src/core/hle/service/mm/mm_u.cpp50
-rw-r--r--src/core/hle/service/nfc/nfc.cpp53
-rw-r--r--src/core/hle/service/nfp/nfp.cpp270
-rw-r--r--src/core/hle/service/nfp/nfp.h23
-rw-r--r--src/core/hle/service/nifm/nifm.cpp1
-rw-r--r--src/core/hle/service/nim/nim.cpp17
-rw-r--r--src/core/hle/service/npns/npns.cpp88
-rw-r--r--src/core/hle/service/npns/npns.h15
-rw-r--r--src/core/hle/service/ns/ns.cpp55
-rw-r--r--src/core/hle/service/ns/pl_u.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp27
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp72
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp8
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h11
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp12
-rw-r--r--src/core/hle/service/prepo/prepo.cpp21
-rw-r--r--src/core/hle/service/ptm/psm.cpp71
-rw-r--r--src/core/hle/service/ptm/psm.h15
-rw-r--r--src/core/hle/service/service.cpp12
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/hle/service/set/set_cal.cpp3
-rw-r--r--src/core/hle/service/usb/usb.cpp49
-rw-r--r--src/core/hle/service/vi/vi.cpp59
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/elf.cpp32
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/loader/loader.h20
-rw-r--r--src/core/loader/nax.cpp4
-rw-r--r--src/core/loader/nax.h1
-rw-r--r--src/core/loader/nro.cpp55
-rw-r--r--src/core/loader/nro.h5
-rw-r--r--src/core/loader/nso.cpp53
-rw-r--r--src/core/loader/nso.h15
-rw-r--r--src/core/loader/nsp.cpp71
-rw-r--r--src/core/loader/nsp.h6
-rw-r--r--src/core/loader/xci.cpp41
-rw-r--r--src/core/loader/xci.h6
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/core/memory_hook.h15
-rw-r--r--src/core/perf_stats.cpp4
-rw-r--r--src/core/settings.h6
-rw-r--r--src/core/telemetry_session.cpp25
-rw-r--r--src/core/telemetry_session.h8
-rw-r--r--src/tests/core/arm/arm_test_common.cpp13
-rw-r--r--src/tests/core/arm/arm_test_common.h10
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/command_processor.cpp2
-rw-r--r--src/video_core/engines/fermi_2d.cpp55
-rw-r--r--src/video_core/engines/fermi_2d.h22
-rw-r--r--src/video_core/engines/kepler_memory.cpp11
-rw-r--r--src/video_core/engines/kepler_memory.h7
-rw-r--r--src/video_core/engines/maxwell_3d.cpp63
-rw-r--r--src/video_core/engines/maxwell_3d.h94
-rw-r--r--src/video_core/engines/maxwell_compute.cpp6
-rw-r--r--src/video_core/engines/maxwell_dma.cpp78
-rw-r--r--src/video_core/engines/maxwell_dma.h12
-rw-r--r--src/video_core/engines/shader_bytecode.h366
-rw-r--r--src/video_core/engines/shader_header.h5
-rw-r--r--src/video_core/gpu.cpp6
-rw-r--r--src/video_core/macro_interpreter.cpp25
-rw-r--r--src/video_core/macro_interpreter.h17
-rw-r--r--src/video_core/memory_manager.cpp18
-rw-r--r--src/video_core/memory_manager.h8
-rw-r--r--src/video_core/rasterizer_cache.h127
-rw-r--r--src/video_core/rasterizer_interface.h11
-rw-r--r--src/video_core/renderer_base.h6
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h9
-rw-r--r--src/video_core/renderer_opengl/gl_primitive_assembler.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp187
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h32
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp848
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h800
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp35
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h57
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp1531
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp117
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h22
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp19
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h13
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp270
-rw-r--r--src/video_core/renderer_opengl/gl_state.h40
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h68
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp25
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h3
-rw-r--r--src/video_core/renderer_opengl/utils.cpp38
-rw-r--r--src/video_core/renderer_opengl/utils.h15
-rw-r--r--src/video_core/surface.cpp483
-rw-r--r--src/video_core/surface.h466
-rw-r--r--src/video_core/textures/astc.cpp32
-rw-r--r--src/video_core/textures/astc.h2
-rw-r--r--src/video_core/textures/decoders.cpp252
-rw-r--r--src/video_core/textures/decoders.h38
-rw-r--r--src/video_core/textures/texture.h36
-rw-r--r--src/video_core/utils.h22
-rw-r--r--src/web_service/CMakeLists.txt4
-rw-r--r--src/web_service/telemetry_json.cpp107
-rw-r--r--src/web_service/telemetry_json.h25
-rw-r--r--src/web_service/verify_login.cpp1
-rw-r--r--src/web_service/web_backend.cpp235
-rw-r--r--src/web_service/web_backend.h58
-rw-r--r--src/yuzu/CMakeLists.txt12
-rw-r--r--src/yuzu/bootmanager.cpp77
-rw-r--r--src/yuzu/bootmanager.h10
-rw-r--r--src/yuzu/compatdb.cpp27
-rw-r--r--src/yuzu/compatdb.h4
-rw-r--r--src/yuzu/configuration/config.cpp32
-rw-r--r--src/yuzu/configuration/config.h14
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui23
-rw-r--r--src/yuzu/configuration/configure_general.cpp2
-rw-r--r--src/yuzu/configuration/configure_general.ui29
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.ui4
-rw-r--r--src/yuzu/configuration/configure_input.cpp89
-rw-r--r--src/yuzu/configuration/configure_input.h9
-rw-r--r--src/yuzu/configuration/configure_input.ui28
-rw-r--r--src/yuzu/configuration/configure_system.cpp290
-rw-r--r--src/yuzu/configuration/configure_system.h48
-rw-r--r--src/yuzu/configuration/configure_system.ui252
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp33
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints_p.h2
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp7
-rw-r--r--src/yuzu/debugger/wait_tree.cpp13
-rw-r--r--src/yuzu/debugger/wait_tree.h1
-rw-r--r--src/yuzu/game_list.cpp17
-rw-r--r--src/yuzu/game_list_worker.cpp59
-rw-r--r--src/yuzu/game_list_worker.h2
-rw-r--r--src/yuzu/main.cpp315
-rw-r--r--src/yuzu/main.h13
-rw-r--r--src/yuzu/main.ui45
-rw-r--r--src/yuzu/util/limitable_input_dialog.cpp59
-rw-r--r--src/yuzu/util/limitable_input_dialog.h31
-rw-r--r--src/yuzu_cmd/config.cpp15
-rw-r--r--src/yuzu_cmd/default_ini.h8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp50
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h12
-rw-r--r--src/yuzu_cmd/yuzu.cpp21
276 files changed, 13526 insertions, 4583 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 6f0ff953a..23e5d3f10 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -30,7 +30,7 @@ public:
30 return info; 30 return info;
31 } 31 }
32 32
33 VoiceInfo& Info() { 33 VoiceInfo& GetInfo() {
34 return info; 34 return info;
35 } 35 }
36 36
@@ -51,9 +51,30 @@ private:
51 VoiceInfo info{}; 51 VoiceInfo info{};
52}; 52};
53 53
54class AudioRenderer::EffectState {
55public:
56 const EffectOutStatus& GetOutStatus() const {
57 return out_status;
58 }
59
60 const EffectInStatus& GetInfo() const {
61 return info;
62 }
63
64 EffectInStatus& GetInfo() {
65 return info;
66 }
67
68 void UpdateState();
69
70private:
71 EffectOutStatus out_status{};
72 EffectInStatus info{};
73};
54AudioRenderer::AudioRenderer(AudioRendererParameter params, 74AudioRenderer::AudioRenderer(AudioRendererParameter params,
55 Kernel::SharedPtr<Kernel::Event> buffer_event) 75 Kernel::SharedPtr<Kernel::Event> buffer_event)
56 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { 76 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
77 effects(params.effect_count) {
57 78
58 audio_out = std::make_unique<AudioCore::AudioOut>(); 79 audio_out = std::make_unique<AudioCore::AudioOut>();
59 stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer", 80 stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
@@ -96,11 +117,29 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
96 memory_pool_count * sizeof(MemoryPoolInfo)); 117 memory_pool_count * sizeof(MemoryPoolInfo));
97 118
98 // Copy VoiceInfo structs 119 // Copy VoiceInfo structs
99 std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + 120 std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
100 config.voice_resource_size}; 121 config.memory_pools_size + config.voice_resource_size};
101 for (auto& voice : voices) { 122 for (auto& voice : voices) {
102 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); 123 std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
103 offset += sizeof(VoiceInfo); 124 voice_offset += sizeof(VoiceInfo);
125 }
126
127 std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
128 config.memory_pools_size + config.voice_resource_size +
129 config.voices_size};
130 for (auto& effect : effects) {
131 std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
132 effect_offset += sizeof(EffectInStatus);
133 }
134
135 // Update memory pool state
136 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
137 for (std::size_t index = 0; index < memory_pool.size(); ++index) {
138 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
139 memory_pool[index].state = MemoryPoolStates::Attached;
140 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
141 memory_pool[index].state = MemoryPoolStates::Detached;
142 }
104 } 143 }
105 144
106 // Update voices 145 // Update voices
@@ -114,14 +153,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
114 } 153 }
115 } 154 }
116 155
117 // Update memory pool state 156 for (auto& effect : effects) {
118 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); 157 effect.UpdateState();
119 for (std::size_t index = 0; index < memory_pool.size(); ++index) {
120 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
121 memory_pool[index].state = MemoryPoolStates::Attached;
122 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
123 memory_pool[index].state = MemoryPoolStates::Detached;
124 }
125 } 158 }
126 159
127 // Release previous buffers and queue next ones for playback 160 // Release previous buffers and queue next ones for playback
@@ -144,6 +177,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
144 voice_out_status_offset += sizeof(VoiceOutStatus); 177 voice_out_status_offset += sizeof(VoiceOutStatus);
145 } 178 }
146 179
180 std::size_t effect_out_status_offset{
181 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
182 response_data.voice_resource_size};
183 for (const auto& effect : effects) {
184 std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
185 sizeof(EffectOutStatus));
186 effect_out_status_offset += sizeof(EffectOutStatus);
187 }
147 return output_params; 188 return output_params;
148} 189}
149 190
@@ -244,11 +285,29 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
244 break; 285 break;
245 } 286 }
246 287
247 samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE); 288 samples =
289 Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE);
248 290
249 is_refresh_pending = false; 291 is_refresh_pending = false;
250} 292}
251 293
294void AudioRenderer::EffectState::UpdateState() {
295 if (info.is_new) {
296 out_status.state = EffectStatus::New;
297 } else {
298 if (info.type == Effect::Aux) {
299 ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
300 "Aux buffers tried to update");
301 ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
302 "Aux buffers tried to update");
303 ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
304 "Aux buffers tried to update");
305 ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
306 "Aux buffers tried to update");
307 }
308 }
309}
310
252static constexpr s16 ClampToS16(s32 value) { 311static constexpr s16 ClampToS16(s32 value) {
253 return static_cast<s16>(std::clamp(value, -32768, 32767)); 312 return static_cast<s16>(std::clamp(value, -32768, 32767));
254} 313}
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index dfef89e1d..046417da3 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -28,6 +28,16 @@ enum class PlayState : u8 {
28 Paused = 2, 28 Paused = 2,
29}; 29};
30 30
31enum class Effect : u8 {
32 None = 0,
33 Aux = 2,
34};
35
36enum class EffectStatus : u8 {
37 None = 0,
38 New = 1,
39};
40
31struct AudioRendererParameter { 41struct AudioRendererParameter {
32 u32_le sample_rate; 42 u32_le sample_rate;
33 u32_le sample_count; 43 u32_le sample_count;
@@ -128,6 +138,43 @@ struct VoiceOutStatus {
128}; 138};
129static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); 139static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
130 140
141struct AuxInfo {
142 std::array<u8, 24> input_mix_buffers;
143 std::array<u8, 24> output_mix_buffers;
144 u32_le mix_buffer_count;
145 u32_le sample_rate; // Stored in the aux buffer currently
146 u32_le sampe_count;
147 u64_le send_buffer_info;
148 u64_le send_buffer_base;
149
150 u64_le return_buffer_info;
151 u64_le return_buffer_base;
152};
153static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
154
155struct EffectInStatus {
156 Effect type;
157 u8 is_new;
158 u8 is_enabled;
159 INSERT_PADDING_BYTES(1);
160 u32_le mix_id;
161 u64_le buffer_base;
162 u64_le buffer_sz;
163 s32_le priority;
164 INSERT_PADDING_BYTES(4);
165 union {
166 std::array<u8, 0xa0> raw;
167 AuxInfo aux_info;
168 };
169};
170static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
171
172struct EffectOutStatus {
173 EffectStatus state;
174 INSERT_PADDING_BYTES(0xf);
175};
176static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
177
131struct UpdateDataHeader { 178struct UpdateDataHeader {
132 UpdateDataHeader() {} 179 UpdateDataHeader() {}
133 180
@@ -173,11 +220,13 @@ public:
173 Stream::State GetStreamState() const; 220 Stream::State GetStreamState() const;
174 221
175private: 222private:
223 class EffectState;
176 class VoiceState; 224 class VoiceState;
177 225
178 AudioRendererParameter worker_params; 226 AudioRendererParameter worker_params;
179 Kernel::SharedPtr<Kernel::Event> buffer_event; 227 Kernel::SharedPtr<Kernel::Event> buffer_event;
180 std::vector<VoiceState> voices; 228 std::vector<VoiceState> voices;
229 std::vector<EffectState> effects;
181 std::unique_ptr<AudioOut> audio_out; 230 std::unique_ptr<AudioOut> audio_out;
182 AudioCore::StreamPtr stream; 231 AudioCore::StreamPtr stream;
183}; 232};
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index 392039688..d31a1c844 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -121,7 +121,8 @@ CubebSink::CubebSink(std::string target_device_name) {
121 const auto collection_end{collection.device + collection.count}; 121 const auto collection_end{collection.device + collection.count};
122 const auto device{ 122 const auto device{
123 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { 123 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
124 return target_device_name == info.friendly_name; 124 return info.friendly_name != nullptr &&
125 target_device_name == info.friendly_name;
125 })}; 126 })};
126 if (device != collection_end) { 127 if (device != collection_end) {
127 output_device = device->devid; 128 output_device = device->devid;
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
index d72d67994..2fe0b3aef 100644
--- a/src/audio_core/time_stretch.cpp
+++ b/src/audio_core/time_stretch.cpp
@@ -10,8 +10,7 @@
10 10
11namespace AudioCore { 11namespace AudioCore {
12 12
13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) 13TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
14 : m_sample_rate(sample_rate), m_channel_count(channel_count) {
15 m_sound_touch.setChannels(channel_count); 14 m_sound_touch.setChannels(channel_count);
16 m_sound_touch.setSampleRate(sample_rate); 15 m_sound_touch.setSampleRate(sample_rate);
17 m_sound_touch.setPitch(1.0); 16 m_sound_touch.setPitch(1.0);
@@ -33,10 +32,10 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
33 // We were given actual_samples number of samples, and num_samples were requested from us. 32 // We were given actual_samples number of samples, and num_samples were requested from us.
34 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out); 33 double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
35 34
36 const double max_latency = 1.0; // seconds 35 const double max_latency = 0.25; // seconds
37 const double max_backlog = m_sample_rate * max_latency; 36 const double max_backlog = m_sample_rate * max_latency;
38 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog; 37 const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
39 if (backlog_fullness > 5.0) { 38 if (backlog_fullness > 4.0) {
40 // Too many samples in backlog: Don't push anymore on 39 // Too many samples in backlog: Don't push anymore on
41 num_in = 0; 40 num_in = 0;
42 } 41 }
@@ -50,7 +49,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
50 49
51 // This low-pass filter smoothes out variance in the calculated stretch ratio. 50 // This low-pass filter smoothes out variance in the calculated stretch ratio.
52 // The time-scale determines how responsive this filter is. 51 // The time-scale determines how responsive this filter is.
53 constexpr double lpf_time_scale = 2.0; // seconds 52 constexpr double lpf_time_scale = 0.712; // seconds
54 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale); 53 const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
55 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio); 54 m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
56 55
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
index decd760f1..bb2270b96 100644
--- a/src/audio_core/time_stretch.h
+++ b/src/audio_core/time_stretch.h
@@ -27,7 +27,6 @@ public:
27 27
28private: 28private:
29 u32 m_sample_rate; 29 u32 m_sample_rate;
30 u32 m_channel_count;
31 soundtouch::SoundTouch m_sound_touch; 30 soundtouch::SoundTouch m_sound_touch;
32 double m_stretch_ratio = 1.0; 31 double m_stretch_ratio = 1.0;
33}; 32};
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 8985e4367..eccd8f64a 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -29,7 +29,7 @@ if ($ENV{CI})
29 if (BUILD_VERSION) 29 if (BUILD_VERSION)
30 # This leaves a trailing space on the last word, but we actually want that 30 # This leaves a trailing space on the last word, but we actually want that
31 # because of how it's styled in the title bar. 31 # because of how it's styled in the title bar.
32 set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ") 32 set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
33 else() 33 else()
34 set(BUILD_FULLNAME "") 34 set(BUILD_FULLNAME "")
35 endif() 35 endif()
@@ -64,8 +64,6 @@ add_library(common STATIC
64 logging/text_formatter.cpp 64 logging/text_formatter.cpp
65 logging/text_formatter.h 65 logging/text_formatter.h
66 math_util.h 66 math_util.h
67 memory_util.cpp
68 memory_util.h
69 microprofile.cpp 67 microprofile.cpp
70 microprofile.h 68 microprofile.h
71 microprofileui.h 69 microprofileui.h
diff --git a/src/common/alignment.h b/src/common/alignment.h
index 225770fab..d94a2291f 100644
--- a/src/common/alignment.h
+++ b/src/common/alignment.h
@@ -19,4 +19,16 @@ constexpr T AlignDown(T value, std::size_t size) {
19 return static_cast<T>(value - value % size); 19 return static_cast<T>(value - value % size);
20} 20}
21 21
22template <typename T>
23constexpr bool Is4KBAligned(T value) {
24 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
25 return (value & 0xFFF) == 0;
26}
27
28template <typename T>
29constexpr bool IsWordAligned(T value) {
30 static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
31 return (value & 0b11) == 0;
32}
33
22} // namespace Common 34} // namespace Common
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 548463787..b52492da6 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -15,21 +15,24 @@
15#ifdef _WIN32 15#ifdef _WIN32
16#include <windows.h> 16#include <windows.h>
17// windows.h needs to be included before other windows headers 17// windows.h needs to be included before other windows headers
18#include <commdlg.h> // for GetSaveFileName 18#include <direct.h> // getcwd
19#include <direct.h> // getcwd
20#include <io.h> 19#include <io.h>
21#include <shellapi.h> 20#include <shellapi.h>
22#include <shlobj.h> // for SHGetFolderPath 21#include <shlobj.h> // for SHGetFolderPath
23#include <tchar.h> 22#include <tchar.h>
24#include "common/string_util.h" 23#include "common/string_util.h"
25 24
26// 64 bit offsets for windows 25#ifdef _MSC_VER
26// 64 bit offsets for MSVC
27#define fseeko _fseeki64 27#define fseeko _fseeki64
28#define ftello _ftelli64 28#define ftello _ftelli64
29#define atoll _atoi64 29#define fileno _fileno
30#endif
31
32// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
30#define stat _stat64 33#define stat _stat64
31#define fstat _fstat64 34#define fstat _fstat64
32#define fileno _fileno 35
33#else 36#else
34#ifdef __APPLE__ 37#ifdef __APPLE__
35#include <sys/param.h> 38#include <sys/param.h>
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp
index 589ae5cbf..5b63f9e81 100644
--- a/src/common/hex_util.cpp
+++ b/src/common/hex_util.cpp
@@ -18,6 +18,25 @@ u8 ToHexNibble(char c1) {
18 return 0; 18 return 0;
19} 19}
20 20
21std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
22 std::vector<u8> out(str.size() / 2);
23 if (little_endian) {
24 for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2)
25 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
26 } else {
27 for (std::size_t i = 0; i < str.size(); i += 2)
28 out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
29 }
30 return out;
31}
32
33std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
34 std::string out;
35 for (u8 c : vector)
36 out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
37 return out;
38}
39
21std::array<u8, 16> operator""_array16(const char* str, std::size_t len) { 40std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
22 if (len != 32) { 41 if (len != 32) {
23 LOG_ERROR(Common, 42 LOG_ERROR(Common,
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index 863a5ccd9..68f003cb6 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <string> 9#include <string>
10#include <vector>
10#include <fmt/format.h> 11#include <fmt/format.h>
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
@@ -14,6 +15,8 @@ namespace Common {
14 15
15u8 ToHexNibble(char c1); 16u8 ToHexNibble(char c1);
16 17
18std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
19
17template <std::size_t Size, bool le = false> 20template <std::size_t Size, bool le = false>
18std::array<u8, Size> HexStringToArray(std::string_view str) { 21std::array<u8, Size> HexStringToArray(std::string_view str) {
19 std::array<u8, Size> out{}; 22 std::array<u8, Size> out{};
@@ -27,6 +30,8 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
27 return out; 30 return out;
28} 31}
29 32
33std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
34
30template <std::size_t Size> 35template <std::size_t Size>
31std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { 36std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
32 std::string out; 37 std::string out;
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 31ad72f38..5753b871a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -203,6 +203,7 @@ void DebuggerBackend::Write(const Entry& entry) {
203 SUB(Service, NFP) \ 203 SUB(Service, NFP) \
204 SUB(Service, NIFM) \ 204 SUB(Service, NIFM) \
205 SUB(Service, NIM) \ 205 SUB(Service, NIM) \
206 SUB(Service, NPNS) \
206 SUB(Service, NS) \ 207 SUB(Service, NS) \
207 SUB(Service, NVDRV) \ 208 SUB(Service, NVDRV) \
208 SUB(Service, PCIE) \ 209 SUB(Service, PCIE) \
@@ -211,10 +212,12 @@ void DebuggerBackend::Write(const Entry& entry) {
211 SUB(Service, PM) \ 212 SUB(Service, PM) \
212 SUB(Service, PREPO) \ 213 SUB(Service, PREPO) \
213 SUB(Service, PSC) \ 214 SUB(Service, PSC) \
215 SUB(Service, PSM) \
214 SUB(Service, SET) \ 216 SUB(Service, SET) \
215 SUB(Service, SM) \ 217 SUB(Service, SM) \
216 SUB(Service, SPL) \ 218 SUB(Service, SPL) \
217 SUB(Service, SSL) \ 219 SUB(Service, SSL) \
220 SUB(Service, TCAP) \
218 SUB(Service, Time) \ 221 SUB(Service, Time) \
219 SUB(Service, USB) \ 222 SUB(Service, USB) \
220 SUB(Service, VI) \ 223 SUB(Service, VI) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index abbd056ee..d4ec31ec3 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -83,6 +83,7 @@ enum class Class : ClassType {
83 Service_NFP, ///< The NFP service 83 Service_NFP, ///< The NFP service
84 Service_NIFM, ///< The NIFM (Network interface) service 84 Service_NIFM, ///< The NIFM (Network interface) service
85 Service_NIM, ///< The NIM service 85 Service_NIM, ///< The NIM service
86 Service_NPNS, ///< The NPNS service
86 Service_NS, ///< The NS services 87 Service_NS, ///< The NS services
87 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 88 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
88 Service_PCIE, ///< The PCIe service 89 Service_PCIE, ///< The PCIe service
@@ -91,10 +92,12 @@ enum class Class : ClassType {
91 Service_PM, ///< The PM service 92 Service_PM, ///< The PM service
92 Service_PREPO, ///< The PREPO (Play report) service 93 Service_PREPO, ///< The PREPO (Play report) service
93 Service_PSC, ///< The PSC service 94 Service_PSC, ///< The PSC service
95 Service_PSM, ///< The PSM service
94 Service_SET, ///< The SET (Settings) service 96 Service_SET, ///< The SET (Settings) service
95 Service_SM, ///< The SM (Service manager) service 97 Service_SM, ///< The SM (Service manager) service
96 Service_SPL, ///< The SPL service 98 Service_SPL, ///< The SPL service
97 Service_SSL, ///< The SSL service 99 Service_SSL, ///< The SSL service
100 Service_TCAP, ///< The TCAP service.
98 Service_Time, ///< The time service 101 Service_Time, ///< The time service
99 Service_USB, ///< The USB (Universal Serial Bus) service 102 Service_USB, ///< The USB (Universal Serial Bus) service
100 Service_VI, ///< The VI (Video interface) service 103 Service_VI, ///< The VI (Video interface) service
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp
deleted file mode 100644
index 9736fb12a..000000000
--- a/src/common/memory_util.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/memory_util.h"
7
8#ifdef _WIN32
9#include <windows.h>
10// Windows.h needs to be included before psapi.h
11#include <psapi.h>
12#include "common/common_funcs.h"
13#include "common/string_util.h"
14#else
15#include <cstdlib>
16#include <sys/mman.h>
17#endif
18
19#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
20#include <unistd.h>
21#define PAGE_MASK (getpagesize() - 1)
22#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
23#endif
24
25// This is purposely not a full wrapper for virtualalloc/mmap, but it
26// provides exactly the primitive operations that Dolphin needs.
27
28void* AllocateExecutableMemory(std::size_t size, bool low) {
29#if defined(_WIN32)
30 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
31#else
32 static char* map_hint = nullptr;
33#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
34 // This OS has no flag to enforce allocation below the 4 GB boundary,
35 // but if we hint that we want a low address it is very likely we will
36 // get one.
37 // An older version of this code used MAP_FIXED, but that has the side
38 // effect of discarding already mapped pages that happen to be in the
39 // requested virtual memory range (such as the emulated RAM, sometimes).
40 if (low && (!map_hint))
41 map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
42#endif
43 void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
44 MAP_ANON | MAP_PRIVATE
45#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
46 | (low ? MAP_32BIT : 0)
47#endif
48 ,
49 -1, 0);
50#endif /* defined(_WIN32) */
51
52#ifdef _WIN32
53 if (ptr == nullptr) {
54#else
55 if (ptr == MAP_FAILED) {
56 ptr = nullptr;
57#endif
58 LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
59 }
60#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
61 else {
62 if (low) {
63 map_hint += size;
64 map_hint = (char*)round_page(map_hint); /* round up to the next page */
65 }
66 }
67#endif
68
69#if EMU_ARCH_BITS == 64
70 if ((u64)ptr >= 0x80000000 && low == true)
71 LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
72#endif
73
74 return ptr;
75}
76
77void* AllocateMemoryPages(std::size_t size) {
78#ifdef _WIN32
79 void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
80#else
81 void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
82
83 if (ptr == MAP_FAILED)
84 ptr = nullptr;
85#endif
86
87 if (ptr == nullptr)
88 LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
89
90 return ptr;
91}
92
93void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
94#ifdef _WIN32
95 void* ptr = _aligned_malloc(size, alignment);
96#else
97 void* ptr = nullptr;
98#ifdef ANDROID
99 ptr = memalign(alignment, size);
100#else
101 if (posix_memalign(&ptr, alignment, size) != 0)
102 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
103#endif
104#endif
105
106 if (ptr == nullptr)
107 LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
108
109 return ptr;
110}
111
112void FreeMemoryPages(void* ptr, std::size_t size) {
113 if (ptr) {
114#ifdef _WIN32
115 if (!VirtualFree(ptr, 0, MEM_RELEASE))
116 LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
117#else
118 munmap(ptr, size);
119#endif
120 }
121}
122
123void FreeAlignedMemory(void* ptr) {
124 if (ptr) {
125#ifdef _WIN32
126 _aligned_free(ptr);
127#else
128 free(ptr);
129#endif
130 }
131}
132
133void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
134#ifdef _WIN32
135 DWORD oldValue;
136 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
137 LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
138#else
139 mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
140#endif
141}
142
143void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
144#ifdef _WIN32
145 DWORD oldValue;
146 if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
147 &oldValue))
148 LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
149#else
150 mprotect(ptr, size,
151 allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
152#endif
153}
154
155std::string MemUsage() {
156#ifdef _WIN32
157#pragma comment(lib, "psapi")
158 DWORD processID = GetCurrentProcessId();
159 HANDLE hProcess;
160 PROCESS_MEMORY_COUNTERS pmc;
161 std::string Ret;
162
163 // Print information about the memory usage of the process.
164
165 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
166 if (nullptr == hProcess)
167 return "MemUsage Error";
168
169 if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
170 Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
171
172 CloseHandle(hProcess);
173 return Ret;
174#else
175 return "";
176#endif
177}
diff --git a/src/common/memory_util.h b/src/common/memory_util.h
deleted file mode 100644
index aad071979..000000000
--- a/src/common/memory_util.h
+++ /dev/null
@@ -1,21 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <cstddef>
8#include <string>
9
10void* AllocateExecutableMemory(std::size_t size, bool low = true);
11void* AllocateMemoryPages(std::size_t size);
12void FreeMemoryPages(void* ptr, std::size_t size);
13void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
14void FreeAlignedMemory(void* ptr);
15void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
16void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
17std::string MemUsage();
18
19inline int GetPageSize() {
20 return 4096;
21}
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index 9526ca0c6..b916b4866 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -20,7 +20,15 @@ constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
20constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1"; 20constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
21constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2"; 21constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
22 22
23/// A placeholder for empty param packages to avoid empty strings
24/// (they may be recognized as "not set" by some frontend libraries like qt)
25constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
26
23ParamPackage::ParamPackage(const std::string& serialized) { 27ParamPackage::ParamPackage(const std::string& serialized) {
28 if (serialized == EMPTY_PLACEHOLDER) {
29 return;
30 }
31
24 std::vector<std::string> pairs; 32 std::vector<std::string> pairs;
25 Common::SplitString(serialized, PARAM_SEPARATOR, pairs); 33 Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
26 34
@@ -46,7 +54,7 @@ ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : d
46 54
47std::string ParamPackage::Serialize() const { 55std::string ParamPackage::Serialize() const {
48 if (data.empty()) 56 if (data.empty())
49 return ""; 57 return EMPTY_PLACEHOLDER;
50 58
51 std::string result; 59 std::string result;
52 60
@@ -120,4 +128,12 @@ bool ParamPackage::Has(const std::string& key) const {
120 return data.find(key) != data.end(); 128 return data.find(key) != data.end();
121} 129}
122 130
131void ParamPackage::Erase(const std::string& key) {
132 data.erase(key);
133}
134
135void ParamPackage::Clear() {
136 data.clear();
137}
138
123} // namespace Common 139} // namespace Common
diff --git a/src/common/param_package.h b/src/common/param_package.h
index 7842cd4ef..6a0a9b656 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -32,6 +32,8 @@ public:
32 void Set(const std::string& key, int value); 32 void Set(const std::string& key, int value);
33 void Set(const std::string& key, float value); 33 void Set(const std::string& key, float value);
34 bool Has(const std::string& key) const; 34 bool Has(const std::string& key) const;
35 void Erase(const std::string& key);
36 void Clear();
35 37
36private: 38private:
37 DataType data; 39 DataType data;
diff --git a/src/common/telemetry.h b/src/common/telemetry.h
index 8d6ab986b..854a73fae 100644
--- a/src/common/telemetry.h
+++ b/src/common/telemetry.h
@@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
153 153
154 /// Completion method, called once all fields have been visited 154 /// Completion method, called once all fields have been visited
155 virtual void Complete() = 0; 155 virtual void Complete() = 0;
156 virtual bool SubmitTestcase() = 0;
156}; 157};
157 158
158/** 159/**
@@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
178 void Visit(const Field<std::chrono::microseconds>& /*field*/) override {} 179 void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
179 180
180 void Complete() override {} 181 void Complete() override {}
182 bool SubmitTestcase() override {
183 return false;
184 }
181}; 185};
182 186
183/// Appends build-specific information to the given FieldCollection, 187/// Appends build-specific information to the given FieldCollection,
diff --git a/src/common/web_result.h b/src/common/web_result.h
index 969926674..8bfa2141d 100644
--- a/src/common/web_result.h
+++ b/src/common/web_result.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h"
8 9
9namespace Common { 10namespace Common {
10struct WebResult { 11struct WebResult {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e4a676e91..64fdf38cd 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -18,6 +18,8 @@ add_library(core STATIC
18 crypto/encryption_layer.h 18 crypto/encryption_layer.h
19 crypto/key_manager.cpp 19 crypto/key_manager.cpp
20 crypto/key_manager.h 20 crypto/key_manager.h
21 crypto/partition_data_manager.cpp
22 crypto/partition_data_manager.h
21 crypto/ctr_encryption_layer.cpp 23 crypto/ctr_encryption_layer.cpp
22 crypto/ctr_encryption_layer.h 24 crypto/ctr_encryption_layer.h
23 crypto/xts_encryption_layer.cpp 25 crypto/xts_encryption_layer.cpp
@@ -70,6 +72,7 @@ add_library(core STATIC
70 file_sys/vfs_real.cpp 72 file_sys/vfs_real.cpp
71 file_sys/vfs_real.h 73 file_sys/vfs_real.h
72 file_sys/vfs_static.h 74 file_sys/vfs_static.h
75 file_sys/vfs_types.h
73 file_sys/vfs_vector.cpp 76 file_sys/vfs_vector.cpp
74 file_sys/vfs_vector.h 77 file_sys/vfs_vector.h
75 file_sys/xts_archive.cpp 78 file_sys/xts_archive.cpp
@@ -153,6 +156,8 @@ add_library(core STATIC
153 hle/service/am/omm.h 156 hle/service/am/omm.h
154 hle/service/am/spsm.cpp 157 hle/service/am/spsm.cpp
155 hle/service/am/spsm.h 158 hle/service/am/spsm.h
159 hle/service/am/tcap.cpp
160 hle/service/am/tcap.h
156 hle/service/aoc/aoc_u.cpp 161 hle/service/aoc/aoc_u.cpp
157 hle/service/aoc/aoc_u.h 162 hle/service/aoc/aoc_u.h
158 hle/service/apm/apm.cpp 163 hle/service/apm/apm.cpp
@@ -233,6 +238,24 @@ add_library(core STATIC
233 hle/service/hid/irs.h 238 hle/service/hid/irs.h
234 hle/service/hid/xcd.cpp 239 hle/service/hid/xcd.cpp
235 hle/service/hid/xcd.h 240 hle/service/hid/xcd.h
241 hle/service/hid/controllers/controller_base.cpp
242 hle/service/hid/controllers/controller_base.h
243 hle/service/hid/controllers/debug_pad.cpp
244 hle/service/hid/controllers/debug_pad.h
245 hle/service/hid/controllers/gesture.cpp
246 hle/service/hid/controllers/gesture.h
247 hle/service/hid/controllers/keyboard.cpp
248 hle/service/hid/controllers/keyboard.h
249 hle/service/hid/controllers/mouse.cpp
250 hle/service/hid/controllers/mouse.h
251 hle/service/hid/controllers/npad.cpp
252 hle/service/hid/controllers/npad.h
253 hle/service/hid/controllers/stubbed.cpp
254 hle/service/hid/controllers/stubbed.h
255 hle/service/hid/controllers/touchscreen.cpp
256 hle/service/hid/controllers/touchscreen.h
257 hle/service/hid/controllers/xpad.cpp
258 hle/service/hid/controllers/xpad.h
236 hle/service/lbl/lbl.cpp 259 hle/service/lbl/lbl.cpp
237 hle/service/lbl/lbl.h 260 hle/service/lbl/lbl.h
238 hle/service/ldn/ldn.cpp 261 hle/service/ldn/ldn.cpp
@@ -259,6 +282,8 @@ add_library(core STATIC
259 hle/service/nifm/nifm.h 282 hle/service/nifm/nifm.h
260 hle/service/nim/nim.cpp 283 hle/service/nim/nim.cpp
261 hle/service/nim/nim.h 284 hle/service/nim/nim.h
285 hle/service/npns/npns.cpp
286 hle/service/npns/npns.h
262 hle/service/ns/ns.cpp 287 hle/service/ns/ns.cpp
263 hle/service/ns/ns.h 288 hle/service/ns/ns.h
264 hle/service/ns/pl_u.cpp 289 hle/service/ns/pl_u.cpp
@@ -306,6 +331,8 @@ add_library(core STATIC
306 hle/service/prepo/prepo.h 331 hle/service/prepo/prepo.h
307 hle/service/psc/psc.cpp 332 hle/service/psc/psc.cpp
308 hle/service/psc/psc.h 333 hle/service/psc/psc.h
334 hle/service/ptm/psm.cpp
335 hle/service/ptm/psm.h
309 hle/service/service.cpp 336 hle/service/service.cpp
310 hle/service/service.h 337 hle/service/service.h
311 hle/service/set/set.cpp 338 hle/service/set/set.cpp
@@ -397,8 +424,8 @@ create_target_directory_groups(core)
397target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 424target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
398target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) 425target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
399if (ENABLE_WEB_SERVICE) 426if (ENABLE_WEB_SERVICE)
400 add_definitions(-DENABLE_WEB_SERVICE) 427 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
401 target_link_libraries(core PUBLIC json-headers web_service) 428 target_link_libraries(core PRIVATE web_service)
402endif() 429endif()
403 430
404if (ARCHITECTURE_x86_64) 431if (ARCHITECTURE_x86_64)
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 7e978cf7a..4d2491870 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -129,7 +129,7 @@ public:
129}; 129};
130 130
131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 131std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
132 auto& current_process = Core::CurrentProcess(); 132 auto* current_process = Core::CurrentProcess();
133 auto** const page_table = current_process->VMManager().page_table.pointers.data(); 133 auto** const page_table = current_process->VMManager().page_table.pointers.data();
134 134
135 Dynarmic::A64::UserConfig config; 135 Dynarmic::A64::UserConfig config;
@@ -144,7 +144,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
144 144
145 // Multi-process state 145 // Multi-process state
146 config.processor_id = core_index; 146 config.processor_id = core_index;
147 config.global_monitor = &exclusive_monitor->monitor; 147 config.global_monitor = &exclusive_monitor.monitor;
148 148
149 // System registers 149 // System registers
150 config.tpidrro_el0 = &cb->tpidrro_el0; 150 config.tpidrro_el0 = &cb->tpidrro_el0;
@@ -171,10 +171,9 @@ void ARM_Dynarmic::Step() {
171 cb->InterpreterFallback(jit->GetPC(), 1); 171 cb->InterpreterFallback(jit->GetPC(), 1);
172} 172}
173 173
174ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 174ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
175 std::size_t core_index)
176 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, 175 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
177 exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { 176 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
178 ThreadContext ctx{}; 177 ThreadContext ctx{};
179 inner_unicorn.SaveContext(ctx); 178 inner_unicorn.SaveContext(ctx);
180 PageTableChanged(); 179 PageTableChanged();
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 4ee92ee27..512bf8ce9 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -23,7 +23,7 @@ class DynarmicExclusiveMonitor;
23 23
24class ARM_Dynarmic final : public ARM_Interface { 24class ARM_Dynarmic final : public ARM_Interface {
25public: 25public:
26 ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index); 26 ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
27 ~ARM_Dynarmic(); 27 ~ARM_Dynarmic();
28 28
29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory, 29 void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,7 +62,7 @@ private:
62 ARM_Unicorn inner_unicorn; 62 ARM_Unicorn inner_unicorn;
63 63
64 std::size_t core_index; 64 std::size_t core_index;
65 std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; 65 DynarmicExclusiveMonitor& exclusive_monitor;
66 66
67 Memory::PageTable* current_page_table = nullptr; 67 Memory::PageTable* current_page_table = nullptr;
68}; 68};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b6acfb3e4..6d5b5a2d0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -71,9 +71,9 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
71} 71}
72 72
73/// Runs a CPU core while the system is powered on 73/// Runs a CPU core while the system is powered on
74void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { 74void RunCpuCore(Cpu& cpu_state) {
75 while (Core::System::GetInstance().IsPoweredOn()) { 75 while (Core::System::GetInstance().IsPoweredOn()) {
76 cpu_state->RunLoop(true); 76 cpu_state.RunLoop(true);
77 } 77 }
78} 78}
79} // Anonymous namespace 79} // Anonymous namespace
@@ -95,7 +95,7 @@ struct System::Impl {
95 status = ResultStatus::Success; 95 status = ResultStatus::Success;
96 96
97 // Update thread_to_cpu in case Core 0 is run from a different host thread 97 // Update thread_to_cpu in case Core 0 is run from a different host thread
98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 98 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
99 99
100 if (GDBStub::IsServerEnabled()) { 100 if (GDBStub::IsServerEnabled()) {
101 GDBStub::HandlePacket(); 101 GDBStub::HandlePacket();
@@ -136,18 +136,19 @@ struct System::Impl {
136 if (virtual_filesystem == nullptr) 136 if (virtual_filesystem == nullptr)
137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 137 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
138 138
139 kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main")); 139 auto main_process = Kernel::Process::Create(kernel, "main");
140 kernel.MakeCurrentProcess(main_process.get());
140 141
141 cpu_barrier = std::make_shared<CpuBarrier>(); 142 cpu_barrier = std::make_unique<CpuBarrier>();
142 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 143 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
143 for (std::size_t index = 0; index < cpu_cores.size(); ++index) { 144 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
144 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); 145 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
145 } 146 }
146 147
147 telemetry_session = std::make_unique<Core::TelemetrySession>(); 148 telemetry_session = std::make_unique<Core::TelemetrySession>();
148 service_manager = std::make_shared<Service::SM::ServiceManager>(); 149 service_manager = std::make_shared<Service::SM::ServiceManager>();
149 150
150 Service::Init(service_manager, virtual_filesystem); 151 Service::Init(service_manager, *virtual_filesystem);
151 GDBStub::Init(); 152 GDBStub::Init();
152 153
153 renderer = VideoCore::CreateRenderer(emu_window); 154 renderer = VideoCore::CreateRenderer(emu_window);
@@ -159,12 +160,12 @@ struct System::Impl {
159 160
160 // Create threads for CPU cores 1-3, and build thread_to_cpu map 161 // Create threads for CPU cores 1-3, and build thread_to_cpu map
161 // CPU core 0 is run on the main thread 162 // CPU core 0 is run on the main thread
162 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 163 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
163 if (Settings::values.use_multi_core) { 164 if (Settings::values.use_multi_core) {
164 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) { 165 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
165 cpu_core_threads[index] = 166 cpu_core_threads[index] =
166 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); 167 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
167 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; 168 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
168 } 169 }
169 } 170 }
170 171
@@ -184,7 +185,7 @@ struct System::Impl {
184 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 185 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
185 return ResultStatus::ErrorGetLoader; 186 return ResultStatus::ErrorGetLoader;
186 } 187 }
187 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = 188 std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
188 app_loader->LoadKernelSystemMode(); 189 app_loader->LoadKernelSystemMode();
189 190
190 if (system_mode.second != Loader::ResultStatus::Success) { 191 if (system_mode.second != Loader::ResultStatus::Success) {
@@ -244,6 +245,7 @@ struct System::Impl {
244 for (auto& cpu_core : cpu_cores) { 245 for (auto& cpu_core : cpu_cores) {
245 cpu_core.reset(); 246 cpu_core.reset();
246 } 247 }
248 cpu_exclusive_monitor.reset();
247 cpu_barrier.reset(); 249 cpu_barrier.reset();
248 250
249 // Shutdown kernel and core timing 251 // Shutdown kernel and core timing
@@ -281,9 +283,9 @@ struct System::Impl {
281 std::unique_ptr<VideoCore::RendererBase> renderer; 283 std::unique_ptr<VideoCore::RendererBase> renderer;
282 std::unique_ptr<Tegra::GPU> gpu_core; 284 std::unique_ptr<Tegra::GPU> gpu_core;
283 std::shared_ptr<Tegra::DebugContext> debug_context; 285 std::shared_ptr<Tegra::DebugContext> debug_context;
284 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 286 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
285 std::shared_ptr<CpuBarrier> cpu_barrier; 287 std::unique_ptr<CpuBarrier> cpu_barrier;
286 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; 288 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
287 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; 289 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
288 std::size_t active_core{}; ///< Active core, only used in single thread mode 290 std::size_t active_core{}; ///< Active core, only used in single thread mode
289 291
@@ -297,7 +299,7 @@ struct System::Impl {
297 std::string status_details = ""; 299 std::string status_details = "";
298 300
299 /// Map of guest threads to CPU cores 301 /// Map of guest threads to CPU cores
300 std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu; 302 std::map<std::thread::id, Cpu*> thread_to_cpu;
301 303
302 Core::PerfStats perf_stats; 304 Core::PerfStats perf_stats;
303 Core::FrameLimiter frame_limiter; 305 Core::FrameLimiter frame_limiter;
@@ -310,6 +312,10 @@ Cpu& System::CurrentCpuCore() {
310 return impl->CurrentCpuCore(); 312 return impl->CurrentCpuCore();
311} 313}
312 314
315const Cpu& System::CurrentCpuCore() const {
316 return impl->CurrentCpuCore();
317}
318
313System::ResultStatus System::RunLoop(bool tight_loop) { 319System::ResultStatus System::RunLoop(bool tight_loop) {
314 return impl->RunLoop(tight_loop); 320 return impl->RunLoop(tight_loop);
315} 321}
@@ -340,7 +346,11 @@ PerfStatsResults System::GetAndResetPerfStats() {
340 return impl->GetAndResetPerfStats(); 346 return impl->GetAndResetPerfStats();
341} 347}
342 348
343Core::TelemetrySession& System::TelemetrySession() const { 349TelemetrySession& System::TelemetrySession() {
350 return *impl->telemetry_session;
351}
352
353const TelemetrySession& System::TelemetrySession() const {
344 return *impl->telemetry_session; 354 return *impl->telemetry_session;
345} 355}
346 356
@@ -348,30 +358,44 @@ ARM_Interface& System::CurrentArmInterface() {
348 return CurrentCpuCore().ArmInterface(); 358 return CurrentCpuCore().ArmInterface();
349} 359}
350 360
351std::size_t System::CurrentCoreIndex() { 361const ARM_Interface& System::CurrentArmInterface() const {
362 return CurrentCpuCore().ArmInterface();
363}
364
365std::size_t System::CurrentCoreIndex() const {
352 return CurrentCpuCore().CoreIndex(); 366 return CurrentCpuCore().CoreIndex();
353} 367}
354 368
355Kernel::Scheduler& System::CurrentScheduler() { 369Kernel::Scheduler& System::CurrentScheduler() {
356 return *CurrentCpuCore().Scheduler(); 370 return CurrentCpuCore().Scheduler();
357} 371}
358 372
359const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { 373const Kernel::Scheduler& System::CurrentScheduler() const {
360 ASSERT(core_index < NUM_CPU_CORES); 374 return CurrentCpuCore().Scheduler();
361 return impl->cpu_cores[core_index]->Scheduler(); 375}
376
377Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
378 return CpuCore(core_index).Scheduler();
362} 379}
363 380
364Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { 381const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
382 return CpuCore(core_index).Scheduler();
383}
384
385Kernel::Process* System::CurrentProcess() {
365 return impl->kernel.CurrentProcess(); 386 return impl->kernel.CurrentProcess();
366} 387}
367 388
368const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const { 389const Kernel::Process* System::CurrentProcess() const {
369 return impl->kernel.CurrentProcess(); 390 return impl->kernel.CurrentProcess();
370} 391}
371 392
372ARM_Interface& System::ArmInterface(std::size_t core_index) { 393ARM_Interface& System::ArmInterface(std::size_t core_index) {
373 ASSERT(core_index < NUM_CPU_CORES); 394 return CpuCore(core_index).ArmInterface();
374 return impl->cpu_cores[core_index]->ArmInterface(); 395}
396
397const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
398 return CpuCore(core_index).ArmInterface();
375} 399}
376 400
377Cpu& System::CpuCore(std::size_t core_index) { 401Cpu& System::CpuCore(std::size_t core_index) {
@@ -379,10 +403,19 @@ Cpu& System::CpuCore(std::size_t core_index) {
379 return *impl->cpu_cores[core_index]; 403 return *impl->cpu_cores[core_index];
380} 404}
381 405
406const Cpu& System::CpuCore(std::size_t core_index) const {
407 ASSERT(core_index < NUM_CPU_CORES);
408 return *impl->cpu_cores[core_index];
409}
410
382ExclusiveMonitor& System::Monitor() { 411ExclusiveMonitor& System::Monitor() {
383 return *impl->cpu_exclusive_monitor; 412 return *impl->cpu_exclusive_monitor;
384} 413}
385 414
415const ExclusiveMonitor& System::Monitor() const {
416 return *impl->cpu_exclusive_monitor;
417}
418
386Tegra::GPU& System::GPU() { 419Tegra::GPU& System::GPU() {
387 return *impl->gpu_core; 420 return *impl->gpu_core;
388} 421}
diff --git a/src/core/core.h b/src/core/core.h
index f9a3e97e3..cfacceb81 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -129,11 +129,11 @@ public:
129 */ 129 */
130 bool IsPoweredOn() const; 130 bool IsPoweredOn() const;
131 131
132 /** 132 /// Gets a reference to the telemetry session for this emulation session.
133 * Returns a reference to the telemetry session for this emulation session. 133 Core::TelemetrySession& TelemetrySession();
134 * @returns Reference to the telemetry session. 134
135 */ 135 /// Gets a reference to the telemetry session for this emulation session.
136 Core::TelemetrySession& TelemetrySession() const; 136 const Core::TelemetrySession& TelemetrySession() const;
137 137
138 /// Prepare the core emulation for a reschedule 138 /// Prepare the core emulation for a reschedule
139 void PrepareReschedule(); 139 void PrepareReschedule();
@@ -144,21 +144,36 @@ public:
144 /// Gets an ARM interface to the CPU core that is currently running 144 /// Gets an ARM interface to the CPU core that is currently running
145 ARM_Interface& CurrentArmInterface(); 145 ARM_Interface& CurrentArmInterface();
146 146
147 /// Gets an ARM interface to the CPU core that is currently running
148 const ARM_Interface& CurrentArmInterface() const;
149
147 /// Gets the index of the currently running CPU core 150 /// Gets the index of the currently running CPU core
148 std::size_t CurrentCoreIndex(); 151 std::size_t CurrentCoreIndex() const;
149 152
150 /// Gets the scheduler for the CPU core that is currently running 153 /// Gets the scheduler for the CPU core that is currently running
151 Kernel::Scheduler& CurrentScheduler(); 154 Kernel::Scheduler& CurrentScheduler();
152 155
153 /// Gets an ARM interface to the CPU core with the specified index 156 /// Gets the scheduler for the CPU core that is currently running
157 const Kernel::Scheduler& CurrentScheduler() const;
158
159 /// Gets a reference to an ARM interface for the CPU core with the specified index
154 ARM_Interface& ArmInterface(std::size_t core_index); 160 ARM_Interface& ArmInterface(std::size_t core_index);
155 161
162 /// Gets a const reference to an ARM interface from the CPU core with the specified index
163 const ARM_Interface& ArmInterface(std::size_t core_index) const;
164
156 /// Gets a CPU interface to the CPU core with the specified index 165 /// Gets a CPU interface to the CPU core with the specified index
157 Cpu& CpuCore(std::size_t core_index); 166 Cpu& CpuCore(std::size_t core_index);
158 167
159 /// Gets the exclusive monitor 168 /// Gets a CPU interface to the CPU core with the specified index
169 const Cpu& CpuCore(std::size_t core_index) const;
170
171 /// Gets a reference to the exclusive monitor
160 ExclusiveMonitor& Monitor(); 172 ExclusiveMonitor& Monitor();
161 173
174 /// Gets a constant reference to the exclusive monitor
175 const ExclusiveMonitor& Monitor() const;
176
162 /// Gets a mutable reference to the GPU interface 177 /// Gets a mutable reference to the GPU interface
163 Tegra::GPU& GPU(); 178 Tegra::GPU& GPU();
164 179
@@ -172,13 +187,16 @@ public:
172 const VideoCore::RendererBase& Renderer() const; 187 const VideoCore::RendererBase& Renderer() const;
173 188
174 /// Gets the scheduler for the CPU core with the specified index 189 /// Gets the scheduler for the CPU core with the specified index
175 const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); 190 Kernel::Scheduler& Scheduler(std::size_t core_index);
176 191
177 /// Provides a reference to the current process 192 /// Gets the scheduler for the CPU core with the specified index
178 Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); 193 const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
179 194
180 /// Provides a constant reference to the current process. 195 /// Provides a pointer to the current process
181 const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const; 196 Kernel::Process* CurrentProcess();
197
198 /// Provides a constant pointer to the current process.
199 const Kernel::Process* CurrentProcess() const;
182 200
183 /// Provides a reference to the kernel instance. 201 /// Provides a reference to the kernel instance.
184 Kernel::KernelCore& Kernel(); 202 Kernel::KernelCore& Kernel();
@@ -224,6 +242,9 @@ private:
224 /// Returns the currently running CPU core 242 /// Returns the currently running CPU core
225 Cpu& CurrentCpuCore(); 243 Cpu& CurrentCpuCore();
226 244
245 /// Returns the currently running CPU core
246 const Cpu& CurrentCpuCore() const;
247
227 /** 248 /**
228 * Initialize the emulated system. 249 * Initialize the emulated system.
229 * @param emu_window Reference to the host-system window used for video output and keyboard 250 * @param emu_window Reference to the host-system window used for video output and keyboard
@@ -246,7 +267,7 @@ inline TelemetrySession& Telemetry() {
246 return System::GetInstance().TelemetrySession(); 267 return System::GetInstance().TelemetrySession();
247} 268}
248 269
249inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { 270inline Kernel::Process* CurrentProcess() {
250 return System::GetInstance().CurrentProcess(); 271 return System::GetInstance().CurrentProcess();
251} 272}
252 273
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 265f8ed9c..fffda8a99 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -49,10 +49,8 @@ bool CpuBarrier::Rendezvous() {
49 return false; 49 return false;
50} 50}
51 51
52Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 52Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
53 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index) 53 : cpu_barrier{cpu_barrier}, core_index{core_index} {
54 : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
55
56 if (Settings::values.use_cpu_jit) { 54 if (Settings::values.use_cpu_jit) {
57#ifdef ARCHITECTURE_x86_64 55#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); 56 arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
@@ -64,15 +62,15 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
64 arm_interface = std::make_unique<ARM_Unicorn>(); 62 arm_interface = std::make_unique<ARM_Unicorn>();
65 } 63 }
66 64
67 scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface); 65 scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
68} 66}
69 67
70Cpu::~Cpu() = default; 68Cpu::~Cpu() = default;
71 69
72std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { 70std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
73 if (Settings::values.use_cpu_jit) { 71 if (Settings::values.use_cpu_jit) {
74#ifdef ARCHITECTURE_x86_64 72#ifdef ARCHITECTURE_x86_64
75 return std::make_shared<DynarmicExclusiveMonitor>(num_cores); 73 return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
76#else 74#else
77 return nullptr; // TODO(merry): Passthrough exclusive monitor 75 return nullptr; // TODO(merry): Passthrough exclusive monitor
78#endif 76#endif
@@ -83,7 +81,7 @@ std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_core
83 81
84void Cpu::RunLoop(bool tight_loop) { 82void Cpu::RunLoop(bool tight_loop) {
85 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step 83 // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
86 if (!cpu_barrier->Rendezvous()) { 84 if (!cpu_barrier.Rendezvous()) {
87 // If rendezvous failed, session has been killed 85 // If rendezvous failed, session has been killed
88 return; 86 return;
89 } 87 }
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index ee7e04abc..1d2bdc6cd 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -41,8 +41,7 @@ private:
41 41
42class Cpu { 42class Cpu {
43public: 43public:
44 Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 44 Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
45 std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
46 ~Cpu(); 45 ~Cpu();
47 46
48 void RunLoop(bool tight_loop = true); 47 void RunLoop(bool tight_loop = true);
@@ -59,8 +58,12 @@ public:
59 return *arm_interface; 58 return *arm_interface;
60 } 59 }
61 60
62 const std::shared_ptr<Kernel::Scheduler>& Scheduler() const { 61 Kernel::Scheduler& Scheduler() {
63 return scheduler; 62 return *scheduler;
63 }
64
65 const Kernel::Scheduler& Scheduler() const {
66 return *scheduler;
64 } 67 }
65 68
66 bool IsMainCore() const { 69 bool IsMainCore() const {
@@ -71,14 +74,14 @@ public:
71 return core_index; 74 return core_index;
72 } 75 }
73 76
74 static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); 77 static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
75 78
76private: 79private:
77 void Reschedule(); 80 void Reschedule();
78 81
79 std::unique_ptr<ARM_Interface> arm_interface; 82 std::unique_ptr<ARM_Interface> arm_interface;
80 std::shared_ptr<CpuBarrier> cpu_barrier; 83 CpuBarrier& cpu_barrier;
81 std::shared_ptr<Kernel::Scheduler> scheduler; 84 std::unique_ptr<Kernel::Scheduler> scheduler;
82 85
83 std::atomic<bool> reschedule_pending = false; 86 std::atomic<bool> reschedule_pending = false;
84 std::size_t core_index; 87 std::size_t core_index;
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index bf3a70944..904afa039 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -4,23 +4,56 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bitset>
8#include <cctype>
7#include <fstream> 9#include <fstream>
8#include <locale> 10#include <locale>
11#include <map>
9#include <sstream> 12#include <sstream>
10#include <string_view> 13#include <string_view>
11#include <tuple> 14#include <tuple>
12#include <vector> 15#include <vector>
16#include <mbedtls/bignum.h>
17#include <mbedtls/cipher.h>
18#include <mbedtls/cmac.h>
19#include <mbedtls/sha256.h>
20#include "common/common_funcs.h"
13#include "common/common_paths.h" 21#include "common/common_paths.h"
14#include "common/file_util.h" 22#include "common/file_util.h"
15#include "common/hex_util.h" 23#include "common/hex_util.h"
16#include "common/logging/log.h" 24#include "common/logging/log.h"
17#include "core/crypto/aes_util.h" 25#include "core/crypto/aes_util.h"
18#include "core/crypto/key_manager.h" 26#include "core/crypto/key_manager.h"
27#include "core/crypto/partition_data_manager.h"
28#include "core/file_sys/content_archive.h"
29#include "core/file_sys/nca_metadata.h"
30#include "core/file_sys/partition_filesystem.h"
31#include "core/file_sys/registered_cache.h"
32#include "core/hle/service/filesystem/filesystem.h"
19#include "core/loader/loader.h" 33#include "core/loader/loader.h"
20#include "core/settings.h" 34#include "core/settings.h"
21 35
22namespace Core::Crypto { 36namespace Core::Crypto {
23 37
38constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
39
40using namespace Common;
41
42const std::array<SHA256Hash, 2> eticket_source_hashes{
43 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
44 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
45};
46
47const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
48 {{S128KeyType::Master, 0}, "master_key_"},
49 {{S128KeyType::Package1, 0}, "package1_key_"},
50 {{S128KeyType::Package2, 0}, "package2_key_"},
51 {{S128KeyType::Titlekek, 0}, "titlekek_"},
52 {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
53 {{S128KeyType::Keyblob, 0}, "keyblob_key_"},
54 {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
55};
56
24Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { 57Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
25 Key128 out{}; 58 Key128 out{};
26 59
@@ -37,57 +70,136 @@ Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, K
37 return out; 70 return out;
38} 71}
39 72
40boost::optional<Key128> DeriveSDSeed() { 73Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source) {
74 AESCipher<Key128> sbk_cipher(sbk, Mode::ECB);
75 AESCipher<Key128> tsec_cipher(tsec, Mode::ECB);
76 tsec_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
77 sbk_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
78 return source;
79}
80
81Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source) {
82 Key128 master_root;
83 std::memcpy(master_root.data(), keyblob.data(), sizeof(Key128));
84
85 AESCipher<Key128> master_cipher(master_root, Mode::ECB);
86
87 Key128 master{};
88 master_cipher.Transcode(master_source.data(), master_source.size(), master.data(), Op::Decrypt);
89 return master;
90}
91
92std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
93 const Key128& key) {
94 std::array<u8, 0x90> keyblob;
95 AESCipher<Key128> cipher(key, Mode::CTR);
96 cipher.SetIV(std::vector<u8>(encrypted_keyblob.data() + 0x10, encrypted_keyblob.data() + 0x20));
97 cipher.Transcode(encrypted_keyblob.data() + 0x20, keyblob.size(), keyblob.data(), Op::Decrypt);
98 return keyblob;
99}
100
101void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
102 const auto kek_generation_source =
103 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
104 const auto key_generation_source =
105 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
106
107 if (HasKey(S128KeyType::Master, crypto_revision)) {
108 for (auto kak_type :
109 {KeyAreaKeyType::Application, KeyAreaKeyType::Ocean, KeyAreaKeyType::System}) {
110 if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
111 static_cast<u64>(kak_type))) {
112 const auto source =
113 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
114 static_cast<u64>(kak_type));
115 const auto kek =
116 GenerateKeyEncryptionKey(source, GetKey(S128KeyType::Master, crypto_revision),
117 kek_generation_source, key_generation_source);
118 SetKey(S128KeyType::KeyArea, kek, crypto_revision, static_cast<u64>(kak_type));
119 }
120 }
121
122 AESCipher<Key128> master_cipher(GetKey(S128KeyType::Master, crypto_revision), Mode::ECB);
123 for (auto key_type : {SourceKeyType::Titlekek, SourceKeyType::Package2}) {
124 if (HasKey(S128KeyType::Source, static_cast<u64>(key_type))) {
125 Key128 key{};
126 master_cipher.Transcode(
127 GetKey(S128KeyType::Source, static_cast<u64>(key_type)).data(), key.size(),
128 key.data(), Op::Decrypt);
129 SetKey(key_type == SourceKeyType::Titlekek ? S128KeyType::Titlekek
130 : S128KeyType::Package2,
131 key, crypto_revision);
132 }
133 }
134 }
135}
136
137Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
138 AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
139 Key128 mac_key{};
140 mac_cipher.Transcode(mac_source.data(), mac_key.size(), mac_key.data(), Op::Decrypt);
141 return mac_key;
142}
143
144std::optional<Key128> DeriveSDSeed() {
41 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + 145 const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
42 "/system/save/8000000000000043", 146 "/system/save/8000000000000043",
43 "rb+"); 147 "rb+");
44 if (!save_43.IsOpen()) 148 if (!save_43.IsOpen())
45 return boost::none; 149 return {};
150
46 const FileUtil::IOFile sd_private( 151 const FileUtil::IOFile sd_private(
47 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); 152 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
48 if (!sd_private.IsOpen()) 153 if (!sd_private.IsOpen())
49 return boost::none; 154 return {};
50 155
51 sd_private.Seek(0, SEEK_SET);
52 std::array<u8, 0x10> private_seed{}; 156 std::array<u8, 0x10> private_seed{};
53 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10) 157 if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
54 return boost::none; 158 return {};
159 }
55 160
56 std::array<u8, 0x10> buffer{}; 161 std::array<u8, 0x10> buffer{};
57 std::size_t offset = 0; 162 std::size_t offset = 0;
58 for (; offset + 0x10 < save_43.GetSize(); ++offset) { 163 for (; offset + 0x10 < save_43.GetSize(); ++offset) {
59 save_43.Seek(offset, SEEK_SET); 164 if (!save_43.Seek(offset, SEEK_SET)) {
165 return {};
166 }
167
60 save_43.ReadBytes(buffer.data(), buffer.size()); 168 save_43.ReadBytes(buffer.data(), buffer.size());
61 if (buffer == private_seed) 169 if (buffer == private_seed) {
62 break; 170 break;
171 }
63 } 172 }
64 173
65 if (offset + 0x10 >= save_43.GetSize()) 174 if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
66 return boost::none; 175 return {};
176 }
67 177
68 Key128 seed{}; 178 Key128 seed{};
69 save_43.Seek(offset + 0x10, SEEK_SET); 179 if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
70 save_43.ReadBytes(seed.data(), seed.size()); 180 return {};
181 }
71 return seed; 182 return seed;
72} 183}
73 184
74Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) { 185Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
75 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK))) 186 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)))
76 return Loader::ResultStatus::ErrorMissingSDKEKSource; 187 return Loader::ResultStatus::ErrorMissingSDKEKSource;
77 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration))) 188 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)))
78 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; 189 return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
79 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) 190 if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
80 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; 191 return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
81 192
82 const auto sd_kek_source = 193 const auto sd_kek_source =
83 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)); 194 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
84 const auto aes_kek_gen = 195 const auto aes_kek_gen =
85 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)); 196 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
86 const auto aes_key_gen = 197 const auto aes_key_gen =
87 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)); 198 keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
88 const auto master_00 = keys.GetKey(S128KeyType::Master); 199 const auto master_00 = keys.GetKey(S128KeyType::Master);
89 const auto sd_kek = 200 const auto sd_kek =
90 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); 201 GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
202 keys.SetKey(S128KeyType::SDKek, sd_kek);
91 203
92 if (!keys.HasKey(S128KeyType::SDSeed)) 204 if (!keys.HasKey(S128KeyType::SDSeed))
93 return Loader::ResultStatus::ErrorMissingSDSeed; 205 return Loader::ResultStatus::ErrorMissingSDSeed;
@@ -118,9 +230,147 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
118 return source; ///< Return unaltered source to satisfy output requirement. 230 return source; ///< Return unaltered source to satisfy output requirement.
119 }); 231 });
120 232
233 keys.SetKey(S256KeyType::SDKey, sd_keys[0], static_cast<u64>(SDKeyType::Save));
234 keys.SetKey(S256KeyType::SDKey, sd_keys[1], static_cast<u64>(SDKeyType::NCA));
235
121 return Loader::ResultStatus::Success; 236 return Loader::ResultStatus::Success;
122} 237}
123 238
239std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
240 if (!ticket_save.IsOpen())
241 return {};
242
243 std::vector<u8> buffer(ticket_save.GetSize());
244 if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
245 return {};
246 }
247
248 std::vector<TicketRaw> out;
249 u32 magic{};
250 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
251 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
252 buffer[offset + 3] == 0x0) {
253 out.emplace_back();
254 auto& next = out.back();
255 std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
256 offset += next.size();
257 }
258 }
259
260 return out;
261}
262
263template <size_t size>
264static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
265 const std::array<u8, size>& rhs) {
266 std::array<u8, size> out{};
267 std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(), std::bit_xor<>());
268 return out;
269}
270
271template <size_t target_size, size_t in_size>
272static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
273 // Avoids truncation overflow within the loop below.
274 static_assert(target_size <= 0xFF);
275
276 std::array<u8, in_size + 4> seed_exp{};
277 std::memcpy(seed_exp.data(), seed.data(), in_size);
278
279 std::vector<u8> out;
280 size_t i = 0;
281 while (out.size() < target_size) {
282 out.resize(out.size() + 0x20);
283 seed_exp[in_size + 3] = static_cast<u8>(i);
284 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
285 ++i;
286 }
287
288 std::array<u8, target_size> target;
289 std::memcpy(target.data(), out.data(), target_size);
290 return target;
291}
292
293template <size_t size>
294static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
295 u64 offset = 0;
296 for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
297 if (data[i] == 0x1) {
298 offset = i + 1;
299 break;
300 } else if (data[i] != 0x0) {
301 return {};
302 }
303 }
304
305 return offset;
306}
307
308std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
309 const RSAKeyPair<2048>& key) {
310 u32 cert_authority;
311 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
312 if (cert_authority == 0)
313 return {};
314 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
315 LOG_INFO(Crypto,
316 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
317 cert_authority);
318 }
319
320 Key128 rights_id;
321 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
322
323 if (rights_id == Key128{})
324 return {};
325
326 Key128 key_temp{};
327
328 if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
329 std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
330 return std::make_pair(rights_id, key_temp);
331 }
332
333 mbedtls_mpi D; // RSA Private Exponent
334 mbedtls_mpi N; // RSA Modulus
335 mbedtls_mpi S; // Input
336 mbedtls_mpi M; // Output
337
338 mbedtls_mpi_init(&D);
339 mbedtls_mpi_init(&N);
340 mbedtls_mpi_init(&S);
341 mbedtls_mpi_init(&M);
342
343 mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
344 mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
345 mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
346
347 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
348
349 std::array<u8, 0x100> rsa_step;
350 mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
351
352 u8 m_0 = rsa_step[0];
353 std::array<u8, 0x20> m_1;
354 std::memcpy(m_1.data(), rsa_step.data() + 0x01, m_1.size());
355 std::array<u8, 0xDF> m_2;
356 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
357
358 if (m_0 != 0)
359 return {};
360
361 m_1 = m_1 ^ MGF1<0x20>(m_2);
362 m_2 = m_2 ^ MGF1<0xDF>(m_1);
363
364 const auto offset = FindTicketOffset(m_2);
365 if (!offset)
366 return {};
367 ASSERT(*offset > 0);
368
369 std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
370
371 return std::make_pair(rights_id, key_temp);
372}
373
124KeyManager::KeyManager() { 374KeyManager::KeyManager() {
125 // Initialize keys 375 // Initialize keys
126 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 376 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -137,6 +387,15 @@ KeyManager::KeyManager() {
137 387
138 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true); 388 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
139 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true); 389 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
390 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
391 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
392}
393
394static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
395 if (base.size() < begin + length)
396 return false;
397 return std::all_of(base.begin() + begin, base.begin() + begin + length,
398 [](u8 c) { return std::isxdigit(c); });
140} 399}
141 400
142void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 401void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -158,6 +417,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
158 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); 417 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
159 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); 418 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
160 419
420 if (out[0].compare(0, 1, "#") == 0)
421 continue;
422
161 if (is_title_keys) { 423 if (is_title_keys) {
162 auto rights_id_raw = Common::HexStringToArray<16>(out[0]); 424 auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
163 u128 rights_id{}; 425 u128 rights_id{};
@@ -174,6 +436,50 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
174 const auto index = s256_file_id.at(out[0]); 436 const auto index = s256_file_id.at(out[0]);
175 Key256 key = Common::HexStringToArray<32>(out[1]); 437 Key256 key = Common::HexStringToArray<32>(out[1]);
176 s256_keys[{index.type, index.field1, index.field2}] = key; 438 s256_keys[{index.type, index.field1, index.field2}] = key;
439 } else if (out[0].compare(0, 8, "keyblob_") == 0 &&
440 out[0].compare(0, 9, "keyblob_k") != 0) {
441 if (!ValidCryptoRevisionString(out[0], 8, 2))
442 continue;
443
444 const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
445 keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
446 } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
447 if (!ValidCryptoRevisionString(out[0], 18, 2))
448 continue;
449
450 const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
451 encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
452 } else {
453 for (const auto& kv : KEYS_VARIABLE_LENGTH) {
454 if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
455 continue;
456 if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
457 const auto index =
458 std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
459 const auto sub = kv.first.second;
460 if (sub == 0) {
461 s128_keys[{kv.first.first, index, 0}] =
462 Common::HexStringToArray<16>(out[1]);
463 } else {
464 s128_keys[{kv.first.first, kv.first.second, index}] =
465 Common::HexStringToArray<16>(out[1]);
466 }
467
468 break;
469 }
470 }
471
472 static constexpr std::array<const char*, 3> kak_names = {
473 "key_area_key_application_", "key_area_key_ocean_", "key_area_key_system_"};
474 for (size_t j = 0; j < kak_names.size(); ++j) {
475 const auto& match = kak_names[j];
476 if (out[0].compare(0, std::strlen(match), match) == 0) {
477 const auto index =
478 std::stoul(out[0].substr(std::strlen(match), 2), nullptr, 16);
479 s128_keys[{S128KeyType::KeyArea, index, j}] =
480 Common::HexStringToArray<16>(out[1]);
481 }
482 }
177 } 483 }
178 } 484 }
179 } 485 }
@@ -187,6 +493,28 @@ void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string&
187 LoadFromFile(dir2 + DIR_SEP + filename, title); 493 LoadFromFile(dir2 + DIR_SEP + filename, title);
188} 494}
189 495
496bool KeyManager::BaseDeriveNecessary() const {
497 const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
498 return !HasKey(key_type, index1, index2);
499 };
500
501 if (check_key_existence(S256KeyType::Header))
502 return true;
503
504 for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
505 if (check_key_existence(S128KeyType::Master, i) ||
506 check_key_existence(S128KeyType::KeyArea, i,
507 static_cast<u64>(KeyAreaKeyType::Application)) ||
508 check_key_existence(S128KeyType::KeyArea, i, static_cast<u64>(KeyAreaKeyType::Ocean)) ||
509 check_key_existence(S128KeyType::KeyArea, i,
510 static_cast<u64>(KeyAreaKeyType::System)) ||
511 check_key_existence(S128KeyType::Titlekek, i))
512 return true;
513 }
514
515 return false;
516}
517
190bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const { 518bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
191 return s128_keys.find({id, field1, field2}) != s128_keys.end(); 519 return s128_keys.find({id, field1, field2}) != s128_keys.end();
192} 520}
@@ -207,13 +535,30 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
207 return s256_keys.at({id, field1, field2}); 535 return s256_keys.at({id, field1, field2});
208} 536}
209 537
210template <std::size_t Size> 538Key256 KeyManager::GetBISKey(u8 partition_id) const {
211void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, 539 Key256 out{};
540
541 for (const auto& bis_type : {BISKeyType::Crypto, BISKeyType::Tweak}) {
542 if (HasKey(S128KeyType::BIS, partition_id, static_cast<u64>(bis_type))) {
543 std::memcpy(
544 out.data() + sizeof(Key128) * static_cast<u64>(bis_type),
545 s128_keys.at({S128KeyType::BIS, partition_id, static_cast<u64>(bis_type)}).data(),
546 sizeof(Key128));
547 }
548 }
549
550 return out;
551}
552
553template <size_t Size>
554void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
212 const std::array<u8, Size>& key) { 555 const std::array<u8, Size>& key) {
213 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); 556 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
214 std::string filename = "title.keys_autogenerated"; 557 std::string filename = "title.keys_autogenerated";
215 if (!title_key) 558 if (category == KeyCategory::Standard)
216 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; 559 filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
560 else if (category == KeyCategory::Console)
561 filename = "console.keys_autogenerated";
217 const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename); 562 const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
218 FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename); 563 FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
219 std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app); 564 std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
@@ -227,7 +572,7 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
227 } 572 }
228 573
229 file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key)); 574 file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
230 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key); 575 AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
231} 576}
232 577
233void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { 578void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
@@ -237,8 +582,15 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
237 Key128 rights_id; 582 Key128 rights_id;
238 std::memcpy(rights_id.data(), &field2, sizeof(u64)); 583 std::memcpy(rights_id.data(), &field2, sizeof(u64));
239 std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64)); 584 std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
240 WriteKeyToFile(true, Common::HexArrayToString(rights_id), key); 585 WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
586 }
587
588 auto category = KeyCategory::Standard;
589 if (id == S128KeyType::Keyblob || id == S128KeyType::KeyblobMAC || id == S128KeyType::TSEC ||
590 id == S128KeyType::SecureBoot || id == S128KeyType::SDSeed || id == S128KeyType::BIS) {
591 category = KeyCategory::Console;
241 } 592 }
593
242 const auto iter2 = std::find_if( 594 const auto iter2 = std::find_if(
243 s128_file_id.begin(), s128_file_id.end(), 595 s128_file_id.begin(), s128_file_id.end(),
244 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { 596 [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
@@ -246,7 +598,30 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
246 std::tie(id, field1, field2); 598 std::tie(id, field1, field2);
247 }); 599 });
248 if (iter2 != s128_file_id.end()) 600 if (iter2 != s128_file_id.end())
249 WriteKeyToFile(false, iter2->first, key); 601 WriteKeyToFile(category, iter2->first, key);
602
603 // Variable cases
604 if (id == S128KeyType::KeyArea) {
605 static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
606 "key_area_key_ocean_{:02X}",
607 "key_area_key_system_{:02X}"};
608 WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
609 } else if (id == S128KeyType::Master) {
610 WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
611 } else if (id == S128KeyType::Package1) {
612 WriteKeyToFile(category, fmt::format("package1_key_{:02X}", field1), key);
613 } else if (id == S128KeyType::Package2) {
614 WriteKeyToFile(category, fmt::format("package2_key_{:02X}", field1), key);
615 } else if (id == S128KeyType::Titlekek) {
616 WriteKeyToFile(category, fmt::format("titlekek_{:02X}", field1), key);
617 } else if (id == S128KeyType::Keyblob) {
618 WriteKeyToFile(category, fmt::format("keyblob_key_{:02X}", field1), key);
619 } else if (id == S128KeyType::KeyblobMAC) {
620 WriteKeyToFile(category, fmt::format("keyblob_mac_key_{:02X}", field1), key);
621 } else if (id == S128KeyType::Source && field1 == static_cast<u64>(SourceKeyType::Keyblob)) {
622 WriteKeyToFile(category, fmt::format("keyblob_key_source_{:02X}", field2), key);
623 }
624
250 s128_keys[{id, field1, field2}] = key; 625 s128_keys[{id, field1, field2}] = key;
251} 626}
252 627
@@ -260,7 +635,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
260 std::tie(id, field1, field2); 635 std::tie(id, field1, field2);
261 }); 636 });
262 if (iter != s256_file_id.end()) 637 if (iter != s256_file_id.end())
263 WriteKeyToFile(false, iter->first, key); 638 WriteKeyToFile(KeyCategory::Standard, iter->first, key);
264 s256_keys[{id, field1, field2}] = key; 639 s256_keys[{id, field1, field2}] = key;
265} 640}
266 641
@@ -286,63 +661,391 @@ void KeyManager::DeriveSDSeedLazy() {
286 return; 661 return;
287 662
288 const auto res = DeriveSDSeed(); 663 const auto res = DeriveSDSeed();
289 if (res != boost::none) 664 if (res)
290 SetKey(S128KeyType::SDSeed, res.get()); 665 SetKey(S128KeyType::SDSeed, *res);
666}
667
668static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
669 Key128 out{};
670
671 mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(),
672 key.size() * 8, source, size, out.data());
673 return out;
674}
675
676void KeyManager::DeriveBase() {
677 if (!BaseDeriveNecessary())
678 return;
679
680 if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
681 return;
682
683 const auto has_bis = [this](u64 id) {
684 return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
685 HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Tweak));
686 };
687
688 const auto copy_bis = [this](u64 id_from, u64 id_to) {
689 SetKey(S128KeyType::BIS,
690 GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Crypto)), id_to,
691 static_cast<u64>(BISKeyType::Crypto));
692
693 SetKey(S128KeyType::BIS,
694 GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Tweak)), id_to,
695 static_cast<u64>(BISKeyType::Tweak));
696 };
697
698 if (has_bis(2) && !has_bis(3))
699 copy_bis(2, 3);
700 else if (has_bis(3) && !has_bis(2))
701 copy_bis(3, 2);
702
703 std::bitset<32> revisions(0xFFFFFFFF);
704 for (size_t i = 0; i < revisions.size(); ++i) {
705 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i) ||
706 encrypted_keyblobs[i] == std::array<u8, 0xB0>{}) {
707 revisions.reset(i);
708 }
709 }
710
711 if (!revisions.any())
712 return;
713
714 const auto sbk = GetKey(S128KeyType::SecureBoot);
715 const auto tsec = GetKey(S128KeyType::TSEC);
716
717 for (size_t i = 0; i < revisions.size(); ++i) {
718 if (!revisions[i])
719 continue;
720
721 // Derive keyblob key
722 const auto key = DeriveKeyblobKey(
723 sbk, tsec, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i));
724
725 SetKey(S128KeyType::Keyblob, key, i);
726
727 // Derive keyblob MAC key
728 if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
729 continue;
730
731 const auto mac_key = DeriveKeyblobMACKey(
732 key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
733 SetKey(S128KeyType::KeyblobMAC, mac_key, i);
734
735 Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
736 if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
737 continue;
738
739 // Decrypt keyblob
740 if (keyblobs[i] == std::array<u8, 0x90>{}) {
741 keyblobs[i] = DecryptKeyblob(encrypted_keyblobs[i], key);
742 WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
743 keyblobs[i]);
744 }
745
746 Key128 package1;
747 std::memcpy(package1.data(), keyblobs[i].data() + 0x80, sizeof(Key128));
748 SetKey(S128KeyType::Package1, package1, i);
749
750 // Derive master key
751 if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master))) {
752 SetKey(S128KeyType::Master,
753 DeriveMasterKey(keyblobs[i], GetKey(S128KeyType::Source,
754 static_cast<u64>(SourceKeyType::Master))),
755 i);
756 }
757 }
758
759 revisions.set();
760 for (size_t i = 0; i < revisions.size(); ++i) {
761 if (!HasKey(S128KeyType::Master, i))
762 revisions.reset(i);
763 }
764
765 if (!revisions.any())
766 return;
767
768 for (size_t i = 0; i < revisions.size(); ++i) {
769 if (!revisions[i])
770 continue;
771
772 // Derive general purpose keys
773 DeriveGeneralPurposeKeys(i);
774 }
775
776 if (HasKey(S128KeyType::Master, 0) &&
777 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)) &&
778 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)) &&
779 HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)) &&
780 HasKey(S256KeyType::HeaderSource)) {
781 const auto header_kek = GenerateKeyEncryptionKey(
782 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)),
783 GetKey(S128KeyType::Master, 0),
784 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)),
785 GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)));
786 SetKey(S128KeyType::HeaderKek, header_kek);
787
788 AESCipher<Key128> header_cipher(header_kek, Mode::ECB);
789 Key256 out = GetKey(S256KeyType::HeaderSource);
790 header_cipher.Transcode(out.data(), out.size(), out.data(), Op::Decrypt);
791 SetKey(S256KeyType::Header, out);
792 }
793}
794
795void KeyManager::DeriveETicket(PartitionDataManager& data) {
796 // ETicket keys
797 const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
798 0x0100000000000033, FileSys::ContentRecordType::Program);
799
800 if (es == nullptr)
801 return;
802
803 const auto exefs = es->GetExeFS();
804 if (exefs == nullptr)
805 return;
806
807 const auto main = exefs->GetFile("main");
808 if (main == nullptr)
809 return;
810
811 const auto bytes = main->ReadAllBytes();
812
813 const auto eticket_kek = FindKeyFromHex16(bytes, eticket_source_hashes[0]);
814 const auto eticket_kekek = FindKeyFromHex16(bytes, eticket_source_hashes[1]);
815
816 const auto seed3 = data.GetRSAKekSeed3();
817 const auto mask0 = data.GetRSAKekMask0();
818
819 if (eticket_kek != Key128{})
820 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
821 if (eticket_kekek != Key128{}) {
822 SetKey(S128KeyType::Source, eticket_kekek,
823 static_cast<size_t>(SourceKeyType::ETicketKekek));
824 }
825 if (seed3 != Key128{})
826 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
827 if (mask0 != Key128{})
828 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
829 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
830 mask0 == Key128{}) {
831 return;
832 }
833
834 Key128 rsa_oaep_kek{};
835 std::transform(seed3.begin(), seed3.end(), mask0.begin(), rsa_oaep_kek.begin(),
836 std::bit_xor<>());
837
838 if (rsa_oaep_kek == Key128{})
839 return;
840
841 SetKey(S128KeyType::Source, rsa_oaep_kek,
842 static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
843
844 Key128 temp_kek{};
845 Key128 temp_kekek{};
846 Key128 eticket_final{};
847
848 // Derive ETicket RSA Kek
849 AESCipher<Key128> es_master(GetKey(S128KeyType::Master), Mode::ECB);
850 es_master.Transcode(rsa_oaep_kek.data(), rsa_oaep_kek.size(), temp_kek.data(), Op::Decrypt);
851 AESCipher<Key128> es_kekek(temp_kek, Mode::ECB);
852 es_kekek.Transcode(eticket_kekek.data(), eticket_kekek.size(), temp_kekek.data(), Op::Decrypt);
853 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
854 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
855
856 if (eticket_final == Key128{})
857 return;
858
859 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
860
861 // Titlekeys
862 data.DecryptProdInfo(GetBISKey(0));
863
864 const auto eticket_extended_kek = data.GetETicketExtendedKek();
865
866 std::vector<u8> extended_iv(0x10);
867 std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
868 std::array<u8, 0x230> extended_dec{};
869 AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
870 rsa_1.SetIV(extended_iv);
871 rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
872 extended_dec.data(), Op::Decrypt);
873
874 RSAKeyPair<2048> rsa_key{};
875 std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
876 std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
877 std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
878
879 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
880 "/system/save/80000000000000e1",
881 "rb+");
882 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
883 "/system/save/80000000000000e2",
884 "rb+");
885
886 const auto blob2 = GetTicketblob(save2);
887 auto res = GetTicketblob(save1);
888 res.insert(res.end(), blob2.begin(), blob2.end());
889
890 for (const auto& raw : res) {
891 const auto pair = ParseTicket(raw, rsa_key);
892 if (!pair)
893 continue;
894 const auto& [rid, key] = *pair;
895 u128 rights_id;
896 std::memcpy(rights_id.data(), rid.data(), rid.size());
897 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
898 }
899}
900
901void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
902 if (key == Key128{})
903 return;
904 SetKey(id, key, field1, field2);
905}
906
907void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
908 if (key == Key256{})
909 return;
910 SetKey(id, key, field1, field2);
911}
912
913void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
914 if (!BaseDeriveNecessary())
915 return;
916
917 if (!data.HasBoot0())
918 return;
919
920 for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
921 if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{})
922 continue;
923 encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
924 WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
925 encrypted_keyblobs[i]);
926 }
927
928 SetKeyWrapped(S128KeyType::Source, data.GetPackage2KeySource(),
929 static_cast<u64>(SourceKeyType::Package2));
930 SetKeyWrapped(S128KeyType::Source, data.GetAESKekGenerationSource(),
931 static_cast<u64>(SourceKeyType::AESKekGeneration));
932 SetKeyWrapped(S128KeyType::Source, data.GetTitlekekSource(),
933 static_cast<u64>(SourceKeyType::Titlekek));
934 SetKeyWrapped(S128KeyType::Source, data.GetMasterKeySource(),
935 static_cast<u64>(SourceKeyType::Master));
936 SetKeyWrapped(S128KeyType::Source, data.GetKeyblobMACKeySource(),
937 static_cast<u64>(SourceKeyType::KeyblobMAC));
938
939 for (size_t i = 0; i < PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH; ++i) {
940 SetKeyWrapped(S128KeyType::Source, data.GetKeyblobKeySource(i),
941 static_cast<u64>(SourceKeyType::Keyblob), i);
942 }
943
944 if (data.HasFuses())
945 SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
946
947 DeriveBase();
948
949 Key128 latest_master{};
950 for (s8 i = 0x1F; i >= 0; --i) {
951 if (GetKey(S128KeyType::Master, static_cast<u8>(i)) != Key128{}) {
952 latest_master = GetKey(S128KeyType::Master, static_cast<u8>(i));
953 break;
954 }
955 }
956
957 const auto masters = data.GetTZMasterKeys(latest_master);
958 for (size_t i = 0; i < masters.size(); ++i) {
959 if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i))
960 SetKey(S128KeyType::Master, masters[i], i);
961 }
962
963 DeriveBase();
964
965 if (!data.HasPackage2())
966 return;
967
968 std::array<Key128, 0x20> package2_keys{};
969 for (size_t i = 0; i < package2_keys.size(); ++i) {
970 if (HasKey(S128KeyType::Package2, i))
971 package2_keys[i] = GetKey(S128KeyType::Package2, i);
972 }
973 data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
974
975 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyApplicationSource(),
976 static_cast<u64>(SourceKeyType::KeyAreaKey),
977 static_cast<u64>(KeyAreaKeyType::Application));
978 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyOceanSource(),
979 static_cast<u64>(SourceKeyType::KeyAreaKey),
980 static_cast<u64>(KeyAreaKeyType::Ocean));
981 SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeySystemSource(),
982 static_cast<u64>(SourceKeyType::KeyAreaKey),
983 static_cast<u64>(KeyAreaKeyType::System));
984 SetKeyWrapped(S128KeyType::Source, data.GetSDKekSource(),
985 static_cast<u64>(SourceKeyType::SDKek));
986 SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDSaveKeySource(),
987 static_cast<u64>(SDKeyType::Save));
988 SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDNCAKeySource(),
989 static_cast<u64>(SDKeyType::NCA));
990 SetKeyWrapped(S128KeyType::Source, data.GetHeaderKekSource(),
991 static_cast<u64>(SourceKeyType::HeaderKek));
992 SetKeyWrapped(S256KeyType::HeaderSource, data.GetHeaderKeySource());
993 SetKeyWrapped(S128KeyType::Source, data.GetAESKeyGenerationSource(),
994 static_cast<u64>(SourceKeyType::AESKeyGeneration));
995
996 DeriveBase();
291} 997}
292 998
293const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { 999const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
294 {"master_key_00", {S128KeyType::Master, 0, 0}},
295 {"master_key_01", {S128KeyType::Master, 1, 0}},
296 {"master_key_02", {S128KeyType::Master, 2, 0}},
297 {"master_key_03", {S128KeyType::Master, 3, 0}},
298 {"master_key_04", {S128KeyType::Master, 4, 0}},
299 {"package1_key_00", {S128KeyType::Package1, 0, 0}},
300 {"package1_key_01", {S128KeyType::Package1, 1, 0}},
301 {"package1_key_02", {S128KeyType::Package1, 2, 0}},
302 {"package1_key_03", {S128KeyType::Package1, 3, 0}},
303 {"package1_key_04", {S128KeyType::Package1, 4, 0}},
304 {"package2_key_00", {S128KeyType::Package2, 0, 0}},
305 {"package2_key_01", {S128KeyType::Package2, 1, 0}},
306 {"package2_key_02", {S128KeyType::Package2, 2, 0}},
307 {"package2_key_03", {S128KeyType::Package2, 3, 0}},
308 {"package2_key_04", {S128KeyType::Package2, 4, 0}},
309 {"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
310 {"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
311 {"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
312 {"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
313 {"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
314 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, 1000 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
315 {"key_area_key_application_00", 1001 {"eticket_rsa_kek_source",
316 {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}}, 1002 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
317 {"key_area_key_application_01", 1003 {"eticket_rsa_kekek_source",
318 {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}}, 1004 {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
319 {"key_area_key_application_02", 1005 {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
320 {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}}, 1006 {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
321 {"key_area_key_application_03", 1007 {"rsa_oaep_kek_generation_source",
322 {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}}, 1008 {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
323 {"key_area_key_application_04", 1009 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
324 {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
325 {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
326 {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
327 {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
328 {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
329 {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
330 {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
331 {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
332 {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
333 {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
334 {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
335 {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
336 {"aes_kek_generation_source", 1010 {"aes_kek_generation_source",
337 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}}, 1011 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
338 {"aes_key_generation_source", 1012 {"aes_key_generation_source",
339 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, 1013 {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
1014 {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
1015 {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
1016 {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
1017 {"key_area_key_application_source",
1018 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1019 static_cast<u64>(KeyAreaKeyType::Application)}},
1020 {"key_area_key_ocean_source",
1021 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1022 static_cast<u64>(KeyAreaKeyType::Ocean)}},
1023 {"key_area_key_system_source",
1024 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1025 static_cast<u64>(KeyAreaKeyType::System)}},
1026 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1027 {"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}},
1028 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1029 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
340 {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, 1030 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
1031 {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
1032 {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
1033 {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
1034 {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
1035 {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
1036 {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
1037 {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
1038 {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
1039 {"header_kek", {S128KeyType::HeaderKek, 0, 0}},
1040 {"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
341}; 1041};
342 1042
343const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { 1043const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
344 {"header_key", {S256KeyType::Header, 0, 0}}, 1044 {"header_key", {S256KeyType::Header, 0, 0}},
345 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, 1045 {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
346 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, 1046 {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
1047 {"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
1048 {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
1049 {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
347}; 1050};
348} // namespace Core::Crypto 1051} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 978eec8dc..22f268c65 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -5,11 +5,19 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <map>
9#include <optional>
8#include <string> 10#include <string>
11
9#include <boost/container/flat_map.hpp> 12#include <boost/container/flat_map.hpp>
10#include <boost/optional.hpp>
11#include <fmt/format.h> 13#include <fmt/format.h>
12#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/crypto/partition_data_manager.h"
16#include "core/file_sys/vfs_types.h"
17
18namespace FileUtil {
19class IOFile;
20}
13 21
14namespace Loader { 22namespace Loader {
15enum class ResultStatus : u16; 23enum class ResultStatus : u16;
@@ -22,13 +30,30 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
22using Key128 = std::array<u8, 0x10>; 30using Key128 = std::array<u8, 0x10>;
23using Key256 = std::array<u8, 0x20>; 31using Key256 = std::array<u8, 0x20>;
24using SHA256Hash = std::array<u8, 0x20>; 32using SHA256Hash = std::array<u8, 0x20>;
33using TicketRaw = std::array<u8, 0x400>;
25 34
26static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); 35static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
27static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); 36static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
37
38template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
39struct RSAKeyPair {
40 std::array<u8, byte_size> encryption_key;
41 std::array<u8, byte_size> decryption_key;
42 std::array<u8, byte_size> modulus;
43 std::array<u8, 4> exponent;
44};
45
46enum class KeyCategory : u8 {
47 Standard,
48 Title,
49 Console,
50};
28 51
29enum class S256KeyType : u64 { 52enum class S256KeyType : u64 {
30 Header, // 53 SDKey, // f1=SDKeyType
31 SDKeySource, // f1=SDKeyType 54 Header, //
55 SDKeySource, // f1=SDKeyType
56 HeaderSource, //
32}; 57};
33 58
34enum class S128KeyType : u64 { 59enum class S128KeyType : u64 {
@@ -41,6 +66,14 @@ enum class S128KeyType : u64 {
41 SDSeed, // 66 SDSeed, //
42 Titlekey, // f1=rights id LSB f2=rights id MSB 67 Titlekey, // f1=rights id LSB f2=rights id MSB
43 Source, // f1=source type, f2= sub id 68 Source, // f1=source type, f2= sub id
69 Keyblob, // f1=crypto revision
70 KeyblobMAC, // f1=crypto revision
71 TSEC, //
72 SecureBoot, //
73 BIS, // f1=partition (0-3), f2=type {crypt, tweak}
74 HeaderKek, //
75 SDKek, //
76 RSAKek, //
44}; 77};
45 78
46enum class KeyAreaKeyType : u8 { 79enum class KeyAreaKeyType : u8 {
@@ -50,9 +83,19 @@ enum class KeyAreaKeyType : u8 {
50}; 83};
51 84
52enum class SourceKeyType : u8 { 85enum class SourceKeyType : u8 {
53 SDKEK, 86 SDKek, //
54 AESKEKGeneration, 87 AESKekGeneration, //
55 AESKeyGeneration, 88 AESKeyGeneration, //
89 RSAOaepKekGeneration, //
90 Master, //
91 Keyblob, // f2=crypto revision
92 KeyAreaKey, // f2=KeyAreaKeyType
93 Titlekek, //
94 Package2, //
95 HeaderKek, //
96 KeyblobMAC, //
97 ETicketKek, //
98 ETicketKekek, //
56}; 99};
57 100
58enum class SDKeyType : u8 { 101enum class SDKeyType : u8 {
@@ -60,6 +103,16 @@ enum class SDKeyType : u8 {
60 NCA, 103 NCA,
61}; 104};
62 105
106enum class BISKeyType : u8 {
107 Crypto,
108 Tweak,
109};
110
111enum class RSAKekType : u8 {
112 Mask0,
113 Seed3,
114};
115
63template <typename KeyType> 116template <typename KeyType>
64struct KeyIndex { 117struct KeyIndex {
65 KeyType type; 118 KeyType type;
@@ -91,6 +144,8 @@ public:
91 Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; 144 Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
92 Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; 145 Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
93 146
147 Key256 GetBISKey(u8 partition_id) const;
148
94 void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); 149 void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
95 void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); 150 void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
96 151
@@ -100,23 +155,51 @@ public:
100 // 8*43 and the private file to exist. 155 // 8*43 and the private file to exist.
101 void DeriveSDSeedLazy(); 156 void DeriveSDSeedLazy();
102 157
158 bool BaseDeriveNecessary() const;
159 void DeriveBase();
160 void DeriveETicket(PartitionDataManager& data);
161
162 void PopulateFromPartitionData(PartitionDataManager& data);
163
103private: 164private:
104 boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys; 165 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
105 boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys; 166 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
167
168 std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
169 std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
106 170
107 bool dev_mode; 171 bool dev_mode;
108 void LoadFromFile(const std::string& filename, bool is_title_keys); 172 void LoadFromFile(const std::string& filename, bool is_title_keys);
109 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, 173 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
110 const std::string& filename, bool title); 174 const std::string& filename, bool title);
111 template <std::size_t Size> 175 template <size_t Size>
112 void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); 176 void WriteKeyToFile(KeyCategory category, std::string_view keyname,
177 const std::array<u8, Size>& key);
178
179 void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
180
181 void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
182 void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
113 183
114 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; 184 static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
115 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id; 185 static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
116}; 186};
117 187
118Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); 188Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
119boost::optional<Key128> DeriveSDSeed(); 189Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source);
120Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys); 190Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source);
191Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source);
192std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
193 const Key128& key);
194
195std::optional<Key128> DeriveSDSeed();
196Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
197
198std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
199
200// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
201// 0x140-0x144 is zero)
202std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
203 const RSAKeyPair<2048>& eticket_extended_key);
121 204
122} // namespace Core::Crypto 205} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
new file mode 100644
index 000000000..ed0775444
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -0,0 +1,594 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5// NOTE TO FUTURE MAINTAINERS:
6// When a new version of switch cryptography is released,
7// hash the new keyblob source and master key and add the hashes to
8// the arrays below.
9
10#include <algorithm>
11#include <array>
12#include <cctype>
13#include <cstring>
14#include <mbedtls/sha256.h>
15#include "common/assert.h"
16#include "common/common_funcs.h"
17#include "common/common_types.h"
18#include "common/hex_util.h"
19#include "common/logging/log.h"
20#include "common/string_util.h"
21#include "common/swap.h"
22#include "core/crypto/key_manager.h"
23#include "core/crypto/partition_data_manager.h"
24#include "core/crypto/xts_encryption_layer.h"
25#include "core/file_sys/vfs.h"
26#include "core/file_sys/vfs_offset.h"
27
28using namespace Common;
29
30namespace Core::Crypto {
31
32struct Package2Header {
33 std::array<u8, 0x100> signature;
34 Key128 header_ctr;
35 std::array<Key128, 4> section_ctr;
36 u32_le magic;
37 u32_le base_offset;
38 INSERT_PADDING_BYTES(4);
39 u8 version_max;
40 u8 version_min;
41 INSERT_PADDING_BYTES(2);
42 std::array<u32_le, 4> section_size;
43 std::array<u32_le, 4> section_offset;
44 std::array<SHA256Hash, 4> section_hash;
45};
46static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
47
48struct INIHeader {
49 u32_le magic;
50 u32_le size;
51 u32_le process_count;
52 INSERT_PADDING_BYTES(4);
53};
54static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
55
56struct SectionHeader {
57 u32_le offset;
58 u32_le size_decompressed;
59 u32_le size_compressed;
60 u32_le attribute;
61};
62static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
63
64struct KIPHeader {
65 u32_le magic;
66 std::array<char, 12> name;
67 u64_le title_id;
68 u32_le category;
69 u8 priority;
70 u8 core;
71 INSERT_PADDING_BYTES(1);
72 u8 flags;
73 std::array<SectionHeader, 6> sections;
74 std::array<u32, 0x20> capabilities;
75};
76static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
77
78const std::array<SHA256Hash, 0x10> source_hashes{
79 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
80 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
81 "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
82 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
83 "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
84 "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
85 "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
86 "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
87 "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
88 "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
89 "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
90 "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
91 "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
92 "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
93 "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
94 "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
95};
96
97const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
98 "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
99 "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
100 "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
101 "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
102 "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
103 "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
104 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
105 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
106
107 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
108 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
109 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
110 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
111 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
112 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
113 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
114 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
115
116 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
117 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
118 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
119 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
120 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
121 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
122 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
123 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
124
125 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
126 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
127 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
128 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
129 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
130 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
131 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
132 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
133};
134
135const std::array<SHA256Hash, 0x20> master_key_hashes{
136 "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
137 "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
138 "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
139 "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
140 "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
141 "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
143 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
144
145 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
146 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
147 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
148 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
149 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
150 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
151 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
152 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
153
154 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
155 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
156 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
157 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
158 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
159 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
160 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
161 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
162
163 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
164 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
165 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
166 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
167 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
168 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
169 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
170 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
171};
172
173static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
174 const auto data_size = in.size() - 0xC;
175
176 u32 compressed_size{};
177 u32 init_index{};
178 u32 additional_size{};
179 std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
180 std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
181 std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
182
183 std::vector<u8> out(in.size() + additional_size);
184
185 if (compressed_size == in.size())
186 std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
187 else
188 std::memcpy(out.data(), in.data(), compressed_size);
189
190 auto index = in.size() - init_index;
191 auto out_index = out.size();
192
193 while (out_index > 0) {
194 --index;
195 auto control = in[index];
196 for (size_t i = 0; i < 8; ++i) {
197 if ((control & 0x80) > 0) {
198 ASSERT(index >= 2);
199 index -= 2;
200 u64 segment_offset = in[index] | in[index + 1] << 8;
201 u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
202 segment_offset &= 0xFFF;
203 segment_offset += 3;
204
205 if (out_index < segment_size)
206 segment_size = out_index;
207
208 ASSERT(out_index >= segment_size);
209
210 out_index -= segment_size;
211
212 for (size_t j = 0; j < segment_size; ++j) {
213 ASSERT(out_index + j + segment_offset < out.size());
214 out[out_index + j] = out[out_index + j + segment_offset];
215 }
216 } else {
217 ASSERT(out_index >= 1);
218 --out_index;
219 --index;
220 out[out_index] = in[index];
221 }
222
223 control <<= 1;
224 if (out_index == 0)
225 return out;
226 }
227 }
228
229 return out;
230}
231
232static u8 CalculateMaxKeyblobSourceHash() {
233 for (s8 i = 0x1F; i >= 0; --i) {
234 if (keyblob_source_hashes[i] != SHA256Hash{})
235 return static_cast<u8>(i + 1);
236 }
237
238 return 0;
239}
240
241const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = CalculateMaxKeyblobSourceHash();
242
243template <size_t key_size = 0x10>
244std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary,
245 const std::array<u8, 0x20>& hash) {
246 if (binary.size() < key_size)
247 return {};
248
249 std::array<u8, 0x20> temp{};
250 for (size_t i = 0; i < binary.size() - key_size; ++i) {
251 mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
252
253 if (temp != hash)
254 continue;
255
256 std::array<u8, key_size> out{};
257 std::memcpy(out.data(), binary.data() + i, key_size);
258 return out;
259 }
260
261 return {};
262}
263
264std::array<u8, 16> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 32> hash) {
265 return FindKeyFromHex<0x10>(binary, hash);
266}
267
268static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<u8>& binary,
269 const Key128& key) {
270 if (binary.size() < 0x10)
271 return {};
272
273 SHA256Hash temp{};
274 Key128 dec_temp{};
275 std::array<Key128, 0x20> out{};
276 AESCipher<Key128> cipher(key, Mode::ECB);
277 for (size_t i = 0; i < binary.size() - 0x10; ++i) {
278 cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
279 mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
280
281 for (size_t k = 0; k < out.size(); ++k) {
282 if (temp == master_key_hashes[k]) {
283 out[k] = dec_temp;
284 break;
285 }
286 }
287 }
288
289 return out;
290}
291
292FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
293 const std::string& name) {
294 auto upper = name;
295 std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
296 for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
297 if (dir->GetFile(fname) != nullptr)
298 return dir->GetFile(fname);
299 }
300
301 return nullptr;
302}
303
304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
306 fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
308 package2({
309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
311 FindFileInDirWithNames(sysdata_dir, "BCPKG2-3-SafeMode-Main"),
312 FindFileInDirWithNames(sysdata_dir, "BCPKG2-4-SafeMode-Sub"),
313 FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
314 FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
315 }),
316 prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
317 secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
318 package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
319 secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
320 : secure_monitor->ReadAllBytes()),
321 package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
322 : package1_decrypted->ReadAllBytes()) {
323}
324
325PartitionDataManager::~PartitionDataManager() = default;
326
327bool PartitionDataManager::HasBoot0() const {
328 return boot0 != nullptr;
329}
330
331FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
332 return boot0;
333}
334
335PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
336 std::size_t index) const {
337 if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
338 return GetEncryptedKeyblobs()[index];
339 return {};
340}
341
342PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
343 if (!HasBoot0())
344 return {};
345
346 EncryptedKeyBlobs out{};
347 for (size_t i = 0; i < out.size(); ++i)
348 boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
349 return out;
350}
351
352std::vector<u8> PartitionDataManager::GetSecureMonitor() const {
353 return secure_monitor_bytes;
354}
355
356std::array<u8, 16> PartitionDataManager::GetPackage2KeySource() const {
357 return FindKeyFromHex(secure_monitor_bytes, source_hashes[2]);
358}
359
360std::array<u8, 16> PartitionDataManager::GetAESKekGenerationSource() const {
361 return FindKeyFromHex(secure_monitor_bytes, source_hashes[3]);
362}
363
364std::array<u8, 16> PartitionDataManager::GetTitlekekSource() const {
365 return FindKeyFromHex(secure_monitor_bytes, source_hashes[5]);
366}
367
368std::array<std::array<u8, 16>, 32> PartitionDataManager::GetTZMasterKeys(
369 std::array<u8, 0x10> master_key) const {
370 return FindEncryptedMasterKeyFromHex(secure_monitor_bytes, master_key);
371}
372
373std::array<u8, 16> PartitionDataManager::GetRSAKekSeed3() const {
374 return FindKeyFromHex(secure_monitor_bytes, source_hashes[14]);
375}
376
377std::array<u8, 16> PartitionDataManager::GetRSAKekMask0() const {
378 return FindKeyFromHex(secure_monitor_bytes, source_hashes[15]);
379}
380
381std::vector<u8> PartitionDataManager::GetPackage1Decrypted() const {
382 return package1_decrypted_bytes;
383}
384
385std::array<u8, 16> PartitionDataManager::GetMasterKeySource() const {
386 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[1]);
387}
388
389std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
390 return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
391}
392
393std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
394 if (keyblob_source_hashes[revision] == SHA256Hash{}) {
395 LOG_WARNING(Crypto,
396 "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
397 revision);
398 }
399 return FindKeyFromHex(package1_decrypted_bytes, keyblob_source_hashes[revision]);
400}
401
402bool PartitionDataManager::HasFuses() const {
403 return fuses != nullptr;
404}
405
406FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const {
407 return fuses;
408}
409
410std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const {
411 if (!HasFuses())
412 return {};
413 Key128 out{};
414 fuses->Read(out.data(), out.size(), 0xA4);
415 return out;
416}
417
418bool PartitionDataManager::HasKFuses() const {
419 return kfuses != nullptr;
420}
421
422FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
423 return kfuses;
424}
425
426bool PartitionDataManager::HasPackage2(Package2Type type) const {
427 return package2.at(static_cast<size_t>(type)) != nullptr;
428}
429
430FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const {
431 return package2.at(static_cast<size_t>(type));
432}
433
434bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
435
436 const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
437 Package2Header temp = header;
438 AESCipher<Key128> cipher(key, Mode::CTR);
439 cipher.SetIV(iv);
440 cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
441 Op::Decrypt);
442 if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
443 header = temp;
444 return true;
445 }
446
447 return false;
448}
449
450void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
451 Package2Type type) {
452 FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
453 package2[static_cast<size_t>(type)],
454 package2[static_cast<size_t>(type)]->GetSize() - 0x4000, 0x4000);
455
456 Package2Header header{};
457 if (file->ReadObject(&header) != sizeof(Package2Header))
458 return;
459
460 std::size_t revision = 0xFF;
461 if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
462 for (std::size_t i = 0; i < package2_keys.size(); ++i) {
463 if (AttemptDecrypt(package2_keys[i], header)) {
464 revision = i;
465 }
466 }
467 }
468
469 if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
470 return;
471
472 const auto a = std::make_shared<FileSys::OffsetVfsFile>(
473 file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
474
475 auto c = a->ReadAllBytes();
476
477 AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480
481 INIHeader ini;
482 std::memcpy(&ini, c.data(), sizeof(INIHeader));
483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
484 return;
485
486 u64 offset = sizeof(INIHeader);
487 for (size_t i = 0; i < ini.process_count; ++i) {
488 KIPHeader kip;
489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
491 return;
492
493 const auto name =
494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
495
496 if (name != "FS" && name != "spl") {
497 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
498 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
499 continue;
500 }
501
502 const u64 initial_offset = sizeof(KIPHeader) + offset;
503 const auto text_begin = c.cbegin() + initial_offset;
504 const auto text_end = text_begin + kip.sections[0].size_compressed;
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
506
507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
509
510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
512
513 std::vector<u8> out;
514 out.reserve(text.size() + rodata.size() + data.size());
515 out.insert(out.end(), text.begin(), text.end());
516 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end());
518
519 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
520 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
521
522 if (name == "FS")
523 package2_fs[static_cast<size_t>(type)] = std::move(out);
524 else if (name == "spl")
525 package2_spl[static_cast<size_t>(type)] = std::move(out);
526 }
527}
528
529const std::vector<u8>& PartitionDataManager::GetPackage2FSDecompressed(Package2Type type) const {
530 return package2_fs.at(static_cast<size_t>(type));
531}
532
533std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyApplicationSource(Package2Type type) const {
534 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[6]);
535}
536
537std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyOceanSource(Package2Type type) const {
538 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[7]);
539}
540
541std::array<u8, 16> PartitionDataManager::GetKeyAreaKeySystemSource(Package2Type type) const {
542 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[8]);
543}
544
545std::array<u8, 16> PartitionDataManager::GetSDKekSource(Package2Type type) const {
546 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[9]);
547}
548
549std::array<u8, 32> PartitionDataManager::GetSDSaveKeySource(Package2Type type) const {
550 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[10]);
551}
552
553std::array<u8, 32> PartitionDataManager::GetSDNCAKeySource(Package2Type type) const {
554 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[11]);
555}
556
557std::array<u8, 16> PartitionDataManager::GetHeaderKekSource(Package2Type type) const {
558 return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[12]);
559}
560
561std::array<u8, 32> PartitionDataManager::GetHeaderKeySource(Package2Type type) const {
562 return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[13]);
563}
564
565const std::vector<u8>& PartitionDataManager::GetPackage2SPLDecompressed(Package2Type type) const {
566 return package2_spl.at(static_cast<size_t>(type));
567}
568
569std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type type) const {
570 return FindKeyFromHex(package2_spl.at(static_cast<size_t>(type)), source_hashes[4]);
571}
572
573bool PartitionDataManager::HasProdInfo() const {
574 return prodinfo != nullptr;
575}
576
577FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const {
578 return prodinfo;
579}
580
581void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
582 if (prodinfo == nullptr)
583 return;
584
585 prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
586}
587
588std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
589 std::array<u8, 0x240> out{};
590 if (prodinfo_decrypted != nullptr)
591 prodinfo_decrypted->Read(out.data(), out.size(), 0x3890);
592 return out;
593}
594} // namespace Core::Crypto
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
new file mode 100644
index 000000000..0ad007c72
--- /dev/null
+++ b/src/core/crypto/partition_data_manager.h
@@ -0,0 +1,109 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_types.h"
9#include "core/file_sys/vfs_types.h"
10
11namespace Core::Crypto {
12
13enum class Package2Type {
14 NormalMain,
15 NormalSub,
16 SafeModeMain,
17 SafeModeSub,
18 RepairMain,
19 RepairSub,
20};
21
22class PartitionDataManager {
23public:
24 static const u8 MAX_KEYBLOB_SOURCE_HASH;
25 static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32;
26 static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0;
27
28 using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>;
29 using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
30
31 explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
32 ~PartitionDataManager();
33
34 // BOOT0
35 bool HasBoot0() const;
36 FileSys::VirtualFile GetBoot0Raw() const;
37 EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
38 EncryptedKeyBlobs GetEncryptedKeyblobs() const;
39 std::vector<u8> GetSecureMonitor() const;
40 std::array<u8, 0x10> GetPackage2KeySource() const;
41 std::array<u8, 0x10> GetAESKekGenerationSource() const;
42 std::array<u8, 0x10> GetTitlekekSource() const;
43 std::array<std::array<u8, 0x10>, 0x20> GetTZMasterKeys(std::array<u8, 0x10> master_key) const;
44 std::array<u8, 0x10> GetRSAKekSeed3() const;
45 std::array<u8, 0x10> GetRSAKekMask0() const;
46 std::vector<u8> GetPackage1Decrypted() const;
47 std::array<u8, 0x10> GetMasterKeySource() const;
48 std::array<u8, 0x10> GetKeyblobMACKeySource() const;
49 std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
50
51 // Fuses
52 bool HasFuses() const;
53 FileSys::VirtualFile GetFusesRaw() const;
54 std::array<u8, 0x10> GetSecureBootKey() const;
55
56 // K-Fuses
57 bool HasKFuses() const;
58 FileSys::VirtualFile GetKFusesRaw() const;
59
60 // Package2
61 bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
62 FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
63 void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
64 Package2Type type);
65 const std::vector<u8>& GetPackage2FSDecompressed(
66 Package2Type type = Package2Type::NormalMain) const;
67 std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
68 Package2Type type = Package2Type::NormalMain) const;
69 std::array<u8, 0x10> GetKeyAreaKeyOceanSource(
70 Package2Type type = Package2Type::NormalMain) const;
71 std::array<u8, 0x10> GetKeyAreaKeySystemSource(
72 Package2Type type = Package2Type::NormalMain) const;
73 std::array<u8, 0x10> GetSDKekSource(Package2Type type = Package2Type::NormalMain) const;
74 std::array<u8, 0x20> GetSDSaveKeySource(Package2Type type = Package2Type::NormalMain) const;
75 std::array<u8, 0x20> GetSDNCAKeySource(Package2Type type = Package2Type::NormalMain) const;
76 std::array<u8, 0x10> GetHeaderKekSource(Package2Type type = Package2Type::NormalMain) const;
77 std::array<u8, 0x20> GetHeaderKeySource(Package2Type type = Package2Type::NormalMain) const;
78 const std::vector<u8>& GetPackage2SPLDecompressed(
79 Package2Type type = Package2Type::NormalMain) const;
80 std::array<u8, 0x10> GetAESKeyGenerationSource(
81 Package2Type type = Package2Type::NormalMain) const;
82
83 // PRODINFO
84 bool HasProdInfo() const;
85 FileSys::VirtualFile GetProdInfoRaw() const;
86 void DecryptProdInfo(std::array<u8, 0x20> bis_key);
87 std::array<u8, 0x240> GetETicketExtendedKek() const;
88
89private:
90 FileSys::VirtualFile boot0;
91 FileSys::VirtualFile fuses;
92 FileSys::VirtualFile kfuses;
93 std::array<FileSys::VirtualFile, 6> package2;
94 FileSys::VirtualFile prodinfo;
95 FileSys::VirtualFile secure_monitor;
96 FileSys::VirtualFile package1_decrypted;
97
98 // Processed
99 std::array<FileSys::VirtualFile, 6> package2_decrypted;
100 FileSys::VirtualFile prodinfo_decrypted;
101 std::vector<u8> secure_monitor_bytes;
102 std::vector<u8> package1_decrypted_bytes;
103 std::array<std::vector<u8>, 6> package2_fs;
104 std::array<std::vector<u8>, 6> package2_spl;
105};
106
107std::array<u8, 0x10> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 0x20> hash);
108
109} // namespace Core::Crypto
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 6102ef476..76a2b7e86 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -10,19 +10,19 @@ namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 sysnand_cache(std::make_shared<RegisteredCache>( 13 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_shared<RegisteredCache>( 15 usrnand_cache(std::make_unique<RegisteredCache>(
16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
17 17
18BISFactory::~BISFactory() = default; 18BISFactory::~BISFactory() = default;
19 19
20std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { 20RegisteredCache* BISFactory::GetSystemNANDContents() const {
21 return sysnand_cache; 21 return sysnand_cache.get();
22} 22}
23 23
24std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const { 24RegisteredCache* BISFactory::GetUserNANDContents() const {
25 return usrnand_cache; 25 return usrnand_cache.get();
26} 26}
27 27
28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { 28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index c352e0925..364d309bd 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -20,8 +20,8 @@ public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 27
@@ -29,8 +29,8 @@ private:
29 VirtualDir nand_root; 29 VirtualDir nand_root;
30 VirtualDir load_root; 30 VirtualDir load_root;
31 31
32 std::shared_ptr<RegisteredCache> sysnand_cache; 32 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::shared_ptr<RegisteredCache> usrnand_cache; 33 std::unique_ptr<RegisteredCache> usrnand_cache;
34}; 34};
35 35
36} // namespace FileSys 36} // namespace FileSys
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 8f5142a07..1ece55731 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -122,14 +122,16 @@ u64 XCI::GetProgramTitleID() const {
122 return secure_partition->GetProgramTitleID(); 122 return secure_partition->GetProgramTitleID();
123} 123}
124 124
125std::shared_ptr<NCA> XCI::GetProgramNCA() const { 125bool XCI::HasProgramNCA() const {
126 return program; 126 return program != nullptr;
127} 127}
128 128
129VirtualFile XCI::GetProgramNCAFile() const { 129VirtualFile XCI::GetProgramNCAFile() const {
130 if (GetProgramNCA() == nullptr) 130 if (!HasProgramNCA()) {
131 return nullptr; 131 return nullptr;
132 return GetProgramNCA()->GetBaseFile(); 132 }
133
134 return program->GetBaseFile();
133} 135}
134 136
135const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { 137const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
@@ -166,10 +168,6 @@ VirtualDir XCI::GetParentDirectory() const {
166 return file->GetContainingDirectory(); 168 return file->GetContainingDirectory();
167} 169}
168 170
169bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
170 return false;
171}
172
173Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { 171Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
174 if (partitions[static_cast<std::size_t>(part)] == nullptr) { 172 if (partitions[static_cast<std::size_t>(part)] == nullptr) {
175 return Loader::ResultStatus::ErrorXCIMissingPartition; 173 return Loader::ResultStatus::ErrorXCIMissingPartition;
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index ce514dfa0..8f62571cf 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -80,7 +80,7 @@ public:
80 80
81 u64 GetProgramTitleID() const; 81 u64 GetProgramTitleID() const;
82 82
83 std::shared_ptr<NCA> GetProgramNCA() const; 83 bool HasProgramNCA() const;
84 VirtualFile GetProgramNCAFile() const; 84 VirtualFile GetProgramNCAFile() const;
85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; 85 const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; 86 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
@@ -94,9 +94,6 @@ public:
94 94
95 VirtualDir GetParentDirectory() const override; 95 VirtualDir GetParentDirectory() const override;
96 96
97protected:
98 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
99
100private: 97private:
101 Loader::ResultStatus AddNCAFromPartition(XCIPartition part); 98 Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
102 99
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index aa1b3c17d..b46fe893c 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -4,10 +4,9 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <optional>
7#include <utility> 8#include <utility>
8 9
9#include <boost/optional.hpp>
10
11#include "common/logging/log.h" 10#include "common/logging/log.h"
12#include "core/crypto/aes_util.h" 11#include "core/crypto/aes_util.h"
13#include "core/crypto/ctr_encryption_layer.h" 12#include "core/crypto/ctr_encryption_layer.h"
@@ -97,11 +96,288 @@ union NCASectionHeader {
97}; 96};
98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); 97static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
99 98
100bool IsValidNCA(const NCAHeader& header) { 99static bool IsValidNCA(const NCAHeader& header) {
101 // TODO(DarkLordZach): Add NCA2/NCA0 support. 100 // TODO(DarkLordZach): Add NCA2/NCA0 support.
102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 101 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
103} 102}
104 103
104NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
105 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
106 if (file == nullptr) {
107 status = Loader::ResultStatus::ErrorNullFile;
108 return;
109 }
110
111 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
112 LOG_ERROR(Loader, "File reader errored out during header read.");
113 status = Loader::ResultStatus::ErrorBadNCAHeader;
114 return;
115 }
116
117 if (!HandlePotentialHeaderDecryption()) {
118 return;
119 }
120
121 has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
122 [](char c) { return c != '\0'; });
123
124 const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
125 is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
126 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
127 });
128
129 if (!ReadSections(sections, bktr_base_ivfc_offset)) {
130 return;
131 }
132
133 status = Loader::ResultStatus::Success;
134}
135
136NCA::~NCA() = default;
137
138bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
139 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
140 status = Loader::ResultStatus::ErrorNCA2;
141 return false;
142 }
143
144 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
145 status = Loader::ResultStatus::ErrorNCA0;
146 return false;
147 }
148
149 return true;
150}
151
152bool NCA::HandlePotentialHeaderDecryption() {
153 if (IsValidNCA(header)) {
154 return true;
155 }
156
157 if (!CheckSupportedNCA(header)) {
158 return false;
159 }
160
161 NCAHeader dec_header{};
162 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
163 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
164 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
165 Core::Crypto::Op::Decrypt);
166 if (IsValidNCA(dec_header)) {
167 header = dec_header;
168 encrypted = true;
169 } else {
170 if (!CheckSupportedNCA(dec_header)) {
171 return false;
172 }
173
174 if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
175 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
176 } else {
177 status = Loader::ResultStatus::ErrorMissingHeaderKey;
178 }
179 return false;
180 }
181
182 return true;
183}
184
185std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
186 const std::ptrdiff_t number_sections =
187 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
188 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
189
190 std::vector<NCASectionHeader> sections(number_sections);
191 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
192
193 if (encrypted) {
194 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
195 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
196 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
197 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
198 Core::Crypto::Op::Decrypt);
199 } else {
200 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
201 }
202
203 return sections;
204}
205
206bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
207 for (std::size_t i = 0; i < sections.size(); ++i) {
208 const auto& section = sections[i];
209
210 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
211 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
212 return false;
213 }
214 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
215 if (!ReadPFS0Section(section, header.section_tables[i])) {
216 return false;
217 }
218 }
219 }
220
221 return true;
222}
223
224bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
225 u64 bktr_base_ivfc_offset) {
226 const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
227 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
228 const std::size_t romfs_offset = base_offset + ivfc_offset;
229 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
230 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
231 auto dec = Decrypt(section, raw, romfs_offset);
232
233 if (dec == nullptr) {
234 if (status != Loader::ResultStatus::Success)
235 return false;
236 if (has_rights_id)
237 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
238 else
239 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
240 return false;
241 }
242
243 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
244 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
245 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
246 status = Loader::ResultStatus::ErrorBadBKTRHeader;
247 return false;
248 }
249
250 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
251 section.bktr.subsection.offset) {
252 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
253 return false;
254 }
255
256 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
257 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
258 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
259 return false;
260 }
261
262 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
263 RelocationBlock relocation_block{};
264 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
265 sizeof(RelocationBlock)) {
266 status = Loader::ResultStatus::ErrorBadRelocationBlock;
267 return false;
268 }
269 SubsectionBlock subsection_block{};
270 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
271 sizeof(RelocationBlock)) {
272 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
273 return false;
274 }
275
276 std::vector<RelocationBucketRaw> relocation_buckets_raw(
277 (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
278 if (dec->ReadBytes(relocation_buckets_raw.data(),
279 section.bktr.relocation.size - sizeof(RelocationBlock),
280 section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
281 section.bktr.relocation.size - sizeof(RelocationBlock)) {
282 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
283 return false;
284 }
285
286 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
287 (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
288 if (dec->ReadBytes(subsection_buckets_raw.data(),
289 section.bktr.subsection.size - sizeof(SubsectionBlock),
290 section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
291 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
292 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
293 return false;
294 }
295
296 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
297 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
298 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
299 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
300 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
301 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
302
303 u32 ctr_low;
304 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
305 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
306 subsection_buckets.back().entries.push_back({size, {0}, 0});
307
308 std::optional<Core::Crypto::Key128> key = {};
309 if (encrypted) {
310 if (has_rights_id) {
311 status = Loader::ResultStatus::Success;
312 key = GetTitlekey();
313 if (!key) {
314 status = Loader::ResultStatus::ErrorMissingTitlekey;
315 return false;
316 }
317 } else {
318 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
319 if (!key) {
320 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
321 return false;
322 }
323 }
324 }
325
326 if (bktr_base_romfs == nullptr) {
327 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
328 return false;
329 }
330
331 auto bktr = std::make_shared<BKTR>(
332 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
333 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
334 encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
335 section.raw.section_ctr);
336
337 // BKTR applies to entire IVFC, so make an offset version to level 6
338 files.push_back(std::make_shared<OffsetVfsFile>(
339 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
340 } else {
341 files.push_back(std::move(dec));
342 }
343
344 romfs = files.back();
345 return true;
346}
347
348bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
349 const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
350 section.pfs0.pfs0_header_offset;
351 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
352
353 auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
354 if (dec != nullptr) {
355 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
356
357 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
358 dirs.push_back(std::move(npfs));
359 if (IsDirectoryExeFS(dirs.back()))
360 exefs = dirs.back();
361 } else {
362 if (has_rights_id)
363 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
364 else
365 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
366 return false;
367 }
368 } else {
369 if (status != Loader::ResultStatus::Success)
370 return false;
371 if (has_rights_id)
372 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
373 else
374 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
375 return false;
376 }
377
378 return true;
379}
380
105u8 NCA::GetCryptoRevision() const { 381u8 NCA::GetCryptoRevision() const {
106 u8 master_key_id = header.crypto_type; 382 u8 master_key_id = header.crypto_type;
107 if (header.crypto_type_2 > master_key_id) 383 if (header.crypto_type_2 > master_key_id)
@@ -111,11 +387,11 @@ u8 NCA::GetCryptoRevision() const {
111 return master_key_id; 387 return master_key_id;
112} 388}
113 389
114boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { 390std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
115 const auto master_key_id = GetCryptoRevision(); 391 const auto master_key_id = GetCryptoRevision();
116 392
117 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) 393 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
118 return boost::none; 394 return {};
119 395
120 std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); 396 std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
121 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( 397 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -133,31 +409,31 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
133 static_cast<u8>(type)); 409 static_cast<u8>(type));
134 u128 out_128{}; 410 u128 out_128{};
135 memcpy(out_128.data(), out.data(), 16); 411 memcpy(out_128.data(), out.data(), 16);
136 LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", 412 LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
137 master_key_id, header.key_index, out_128[1], out_128[0]); 413 master_key_id, header.key_index, out_128[1], out_128[0]);
138 414
139 return out; 415 return out;
140} 416}
141 417
142boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() { 418std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
143 const auto master_key_id = GetCryptoRevision(); 419 const auto master_key_id = GetCryptoRevision();
144 420
145 u128 rights_id{}; 421 u128 rights_id{};
146 memcpy(rights_id.data(), header.rights_id.data(), 16); 422 memcpy(rights_id.data(), header.rights_id.data(), 16);
147 if (rights_id == u128{}) { 423 if (rights_id == u128{}) {
148 status = Loader::ResultStatus::ErrorInvalidRightsID; 424 status = Loader::ResultStatus::ErrorInvalidRightsID;
149 return boost::none; 425 return {};
150 } 426 }
151 427
152 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); 428 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
153 if (titlekey == Core::Crypto::Key128{}) { 429 if (titlekey == Core::Crypto::Key128{}) {
154 status = Loader::ResultStatus::ErrorMissingTitlekey; 430 status = Loader::ResultStatus::ErrorMissingTitlekey;
155 return boost::none; 431 return {};
156 } 432 }
157 433
158 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { 434 if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
159 status = Loader::ResultStatus::ErrorMissingTitlekek; 435 status = Loader::ResultStatus::ErrorMissingTitlekek;
160 return boost::none; 436 return {};
161 } 437 }
162 438
163 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( 439 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -167,7 +443,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
167 return titlekey; 443 return titlekey;
168} 444}
169 445
170VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { 446VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
171 if (!encrypted) 447 if (!encrypted)
172 return in; 448 return in;
173 449
@@ -181,25 +457,25 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
181 case NCASectionCryptoType::BKTR: 457 case NCASectionCryptoType::BKTR:
182 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 458 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
183 { 459 {
184 boost::optional<Core::Crypto::Key128> key = boost::none; 460 std::optional<Core::Crypto::Key128> key = {};
185 if (has_rights_id) { 461 if (has_rights_id) {
186 status = Loader::ResultStatus::Success; 462 status = Loader::ResultStatus::Success;
187 key = GetTitlekey(); 463 key = GetTitlekey();
188 if (key == boost::none) { 464 if (!key) {
189 if (status == Loader::ResultStatus::Success) 465 if (status == Loader::ResultStatus::Success)
190 status = Loader::ResultStatus::ErrorMissingTitlekey; 466 status = Loader::ResultStatus::ErrorMissingTitlekey;
191 return nullptr; 467 return nullptr;
192 } 468 }
193 } else { 469 } else {
194 key = GetKeyAreaKey(NCASectionCryptoType::CTR); 470 key = GetKeyAreaKey(NCASectionCryptoType::CTR);
195 if (key == boost::none) { 471 if (!key) {
196 status = Loader::ResultStatus::ErrorMissingKeyAreaKey; 472 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
197 return nullptr; 473 return nullptr;
198 } 474 }
199 } 475 }
200 476
201 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( 477 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
202 std::move(in), key.value(), starting_offset); 478 starting_offset);
203 std::vector<u8> iv(16); 479 std::vector<u8> iv(16);
204 for (u8 i = 0; i < 8; ++i) 480 for (u8 i = 0; i < 8; ++i)
205 iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; 481 iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
@@ -215,256 +491,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
215 } 491 }
216} 492}
217 493
218NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
219 : file(std::move(file_)),
220 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
221 status = Loader::ResultStatus::Success;
222
223 if (file == nullptr) {
224 status = Loader::ResultStatus::ErrorNullFile;
225 return;
226 }
227
228 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
229 LOG_ERROR(Loader, "File reader errored out during header read.");
230 status = Loader::ResultStatus::ErrorBadNCAHeader;
231 return;
232 }
233
234 encrypted = false;
235
236 if (!IsValidNCA(header)) {
237 if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
238 status = Loader::ResultStatus::ErrorNCA2;
239 return;
240 }
241 if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
242 status = Loader::ResultStatus::ErrorNCA0;
243 return;
244 }
245
246 NCAHeader dec_header{};
247 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
248 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
249 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
250 Core::Crypto::Op::Decrypt);
251 if (IsValidNCA(dec_header)) {
252 header = dec_header;
253 encrypted = true;
254 } else {
255 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
256 status = Loader::ResultStatus::ErrorNCA2;
257 return;
258 }
259 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
260 status = Loader::ResultStatus::ErrorNCA0;
261 return;
262 }
263
264 if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
265 status = Loader::ResultStatus::ErrorMissingHeaderKey;
266 else
267 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
268 return;
269 }
270 }
271
272 has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
273 [](char c) { return c == '\0'; }) != header.rights_id.end();
274
275 const std::ptrdiff_t number_sections =
276 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
277 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
278
279 std::vector<NCASectionHeader> sections(number_sections);
280 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
281
282 if (encrypted) {
283 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
284 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
285 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
286 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
287 Core::Crypto::Op::Decrypt);
288 } else {
289 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
290 }
291
292 is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
293 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
294 }) != sections.end();
295 ivfc_offset = 0;
296
297 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
298 auto section = sections[i];
299
300 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
301 const std::size_t base_offset =
302 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
303 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
304 const std::size_t romfs_offset = base_offset + ivfc_offset;
305 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
306 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
307 auto dec = Decrypt(section, raw, romfs_offset);
308
309 if (dec == nullptr) {
310 if (status != Loader::ResultStatus::Success)
311 return;
312 if (has_rights_id)
313 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
314 else
315 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
316 return;
317 }
318
319 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
320 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
321 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
322 status = Loader::ResultStatus::ErrorBadBKTRHeader;
323 return;
324 }
325
326 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
327 section.bktr.subsection.offset) {
328 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
329 return;
330 }
331
332 const u64 size =
333 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
334 header.section_tables[i].media_offset);
335 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
336 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
337 return;
338 }
339
340 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
341 RelocationBlock relocation_block{};
342 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
343 sizeof(RelocationBlock)) {
344 status = Loader::ResultStatus::ErrorBadRelocationBlock;
345 return;
346 }
347 SubsectionBlock subsection_block{};
348 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
349 sizeof(RelocationBlock)) {
350 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
351 return;
352 }
353
354 std::vector<RelocationBucketRaw> relocation_buckets_raw(
355 (section.bktr.relocation.size - sizeof(RelocationBlock)) /
356 sizeof(RelocationBucketRaw));
357 if (dec->ReadBytes(relocation_buckets_raw.data(),
358 section.bktr.relocation.size - sizeof(RelocationBlock),
359 section.bktr.relocation.offset + sizeof(RelocationBlock) -
360 offset) !=
361 section.bktr.relocation.size - sizeof(RelocationBlock)) {
362 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
363 return;
364 }
365
366 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
367 (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
368 sizeof(SubsectionBucketRaw));
369 if (dec->ReadBytes(subsection_buckets_raw.data(),
370 section.bktr.subsection.size - sizeof(SubsectionBlock),
371 section.bktr.subsection.offset + sizeof(SubsectionBlock) -
372 offset) !=
373 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
374 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
375 return;
376 }
377
378 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
379 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
380 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
381 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
382 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
383 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
384
385 u32 ctr_low;
386 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
387 subsection_buckets.back().entries.push_back(
388 {section.bktr.relocation.offset, {0}, ctr_low});
389 subsection_buckets.back().entries.push_back({size, {0}, 0});
390
391 boost::optional<Core::Crypto::Key128> key = boost::none;
392 if (encrypted) {
393 if (has_rights_id) {
394 status = Loader::ResultStatus::Success;
395 key = GetTitlekey();
396 if (key == boost::none) {
397 status = Loader::ResultStatus::ErrorMissingTitlekey;
398 return;
399 }
400 } else {
401 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
402 if (key == boost::none) {
403 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
404 return;
405 }
406 }
407 }
408
409 if (bktr_base_romfs == nullptr) {
410 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
411 return;
412 }
413
414 auto bktr = std::make_shared<BKTR>(
415 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
416 relocation_block, relocation_buckets, subsection_block, subsection_buckets,
417 encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
418 bktr_base_ivfc_offset, section.raw.section_ctr);
419
420 // BKTR applies to entire IVFC, so make an offset version to level 6
421
422 files.push_back(std::make_shared<OffsetVfsFile>(
423 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
424 romfs = files.back();
425 } else {
426 files.push_back(std::move(dec));
427 romfs = files.back();
428 }
429 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
430 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
431 MEDIA_OFFSET_MULTIPLIER) +
432 section.pfs0.pfs0_header_offset;
433 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
434 header.section_tables[i].media_offset);
435 auto dec =
436 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
437 if (dec != nullptr) {
438 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
439
440 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
441 dirs.push_back(std::move(npfs));
442 if (IsDirectoryExeFS(dirs.back()))
443 exefs = dirs.back();
444 } else {
445 if (has_rights_id)
446 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
447 else
448 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
449 return;
450 }
451 } else {
452 if (status != Loader::ResultStatus::Success)
453 return;
454 if (has_rights_id)
455 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
456 else
457 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
458 return;
459 }
460 }
461 }
462
463 status = Loader::ResultStatus::Success;
464}
465
466NCA::~NCA() = default;
467
468Loader::ResultStatus NCA::GetStatus() const { 494Loader::ResultStatus NCA::GetStatus() const {
469 return status; 495 return status;
470} 496}
@@ -519,7 +545,4 @@ u64 NCA::GetBaseIVFCOffset() const {
519 return ivfc_offset; 545 return ivfc_offset;
520} 546}
521 547
522bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
523 return false;
524}
525} // namespace FileSys 548} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f9f66cae9..4bba55607 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -6,9 +6,10 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10#include <vector> 11#include <vector>
11#include <boost/optional.hpp> 12
12#include "common/common_funcs.h" 13#include "common/common_funcs.h"
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "common/swap.h" 15#include "common/swap.h"
@@ -73,8 +74,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; 74 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
74} 75}
75 76
76bool IsValidNCA(const NCAHeader& header);
77
78// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. 77// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
79// After construction, use GetStatus to determine if the file is valid and ready to be used. 78// After construction, use GetStatus to determine if the file is valid and ready to be used.
80class NCA : public ReadOnlyVfsDirectory { 79class NCA : public ReadOnlyVfsDirectory {
@@ -102,14 +101,20 @@ public:
102 // Returns the base ivfc offset used in BKTR patching. 101 // Returns the base ivfc offset used in BKTR patching.
103 u64 GetBaseIVFCOffset() const; 102 u64 GetBaseIVFCOffset() const;
104 103
105protected:
106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
107
108private: 104private:
105 bool CheckSupportedNCA(const NCAHeader& header);
106 bool HandlePotentialHeaderDecryption();
107
108 std::vector<NCASectionHeader> ReadSectionHeaders() const;
109 bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
110 bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
111 u64 bktr_base_ivfc_offset);
112 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
113
109 u8 GetCryptoRevision() const; 114 u8 GetCryptoRevision() const;
110 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 115 std::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
111 boost::optional<Core::Crypto::Key128> GetTitlekey(); 116 std::optional<Core::Crypto::Key128> GetTitlekey();
112 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); 117 VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
113 118
114 std::vector<VirtualDir> dirs; 119 std::vector<VirtualDir> dirs;
115 std::vector<VirtualFile> files; 120 std::vector<VirtualFile> files;
@@ -118,15 +123,15 @@ private:
118 VirtualDir exefs = nullptr; 123 VirtualDir exefs = nullptr;
119 VirtualFile file; 124 VirtualFile file;
120 VirtualFile bktr_base_romfs; 125 VirtualFile bktr_base_romfs;
121 u64 ivfc_offset; 126 u64 ivfc_offset = 0;
122 127
123 NCAHeader header{}; 128 NCAHeader header{};
124 bool has_rights_id{}; 129 bool has_rights_id{};
125 130
126 Loader::ResultStatus status{}; 131 Loader::ResultStatus status{};
127 132
128 bool encrypted; 133 bool encrypted = false;
129 bool is_update; 134 bool is_update = false;
130 135
131 Core::Crypto::KeyManager keys; 136 Core::Crypto::KeyManager keys;
132}; 137};
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
17}; 17};
18 18
19std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
21 application_name.size());
21} 22}
22 23
23std::string LanguageEntry::GetDeveloperName() const { 24std::string LanguageEntry::GetDeveloperName() const {
24 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); 25 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
26 developer_name.size());
25} 27}
26 28
27NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 29NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
56 return raw->title_id; 58 return raw->title_id;
57} 59}
58 60
61u64 NACP::GetDLCBaseTitleId() const {
62 return raw->dlc_base_title_id;
63}
64
59std::string NACP::GetVersionString() const { 65std::string NACP::GetVersionString() const {
60 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size());
61} 68}
62} // namespace FileSys 69} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
79 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const;
82 std::string GetVersionString() const; 83 std::string GetVersionString() const;
83 84
84private: 85private:
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index 2a913ce82..47b7526c7 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -26,6 +26,7 @@
26#include "common/alignment.h" 26#include "common/alignment.h"
27#include "common/assert.h" 27#include "common/assert.h"
28#include "core/file_sys/fsmitm_romfsbuild.h" 28#include "core/file_sys/fsmitm_romfsbuild.h"
29#include "core/file_sys/ips_layer.h"
29#include "core/file_sys/vfs.h" 30#include "core/file_sys/vfs.h"
30#include "core/file_sys/vfs_vector.h" 31#include "core/file_sys/vfs_vector.h"
31 32
@@ -123,7 +124,7 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
123 return count; 124 return count;
124} 125}
125 126
126void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, 127void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext,
127 std::shared_ptr<RomFSBuildDirectoryContext> parent) { 128 std::shared_ptr<RomFSBuildDirectoryContext> parent) {
128 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; 129 std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
129 130
@@ -144,6 +145,9 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
144 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); 145 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
145 child->path = parent->path + "/" + kv.first; 146 child->path = parent->path + "/" + kv.first;
146 147
148 if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
149 continue;
150
147 // Sanity check on path_len 151 // Sanity check on path_len
148 ASSERT(child->path_len < FS_MAX_PATH); 152 ASSERT(child->path_len < FS_MAX_PATH);
149 153
@@ -157,11 +161,24 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
157 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); 161 child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
158 child->path = parent->path + "/" + kv.first; 162 child->path = parent->path + "/" + kv.first;
159 163
164 if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
165 continue;
166
160 // Sanity check on path_len 167 // Sanity check on path_len
161 ASSERT(child->path_len < FS_MAX_PATH); 168 ASSERT(child->path_len < FS_MAX_PATH);
162 169
163 child->source = root_romfs->GetFileRelative(child->path); 170 child->source = root_romfs->GetFileRelative(child->path);
164 171
172 if (ext != nullptr) {
173 const auto ips = ext->GetFileRelative(child->path + ".ips");
174
175 if (ips != nullptr) {
176 auto patched = PatchIPS(child->source, ips);
177 if (patched != nullptr)
178 child->source = std::move(patched);
179 }
180 }
181
165 child->size = child->source->GetSize(); 182 child->size = child->source->GetSize();
166 183
167 AddFile(parent, child); 184 AddFile(parent, child);
@@ -169,7 +186,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
169 } 186 }
170 187
171 for (auto& child : child_dirs) { 188 for (auto& child : child_dirs) {
172 this->VisitDirectory(root_romfs, child); 189 this->VisitDirectory(root_romfs, ext, child);
173 } 190 }
174} 191}
175 192
@@ -208,14 +225,15 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare
208 return true; 225 return true;
209} 226}
210 227
211RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) { 228RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
229 : base(std::move(base_)), ext(std::move(ext_)) {
212 root = std::make_shared<RomFSBuildDirectoryContext>(); 230 root = std::make_shared<RomFSBuildDirectoryContext>();
213 root->path = "\0"; 231 root->path = "\0";
214 directories.emplace(root->path, root); 232 directories.emplace(root->path, root);
215 num_dirs = 1; 233 num_dirs = 1;
216 dir_table_size = 0x18; 234 dir_table_size = 0x18;
217 235
218 VisitDirectory(base, root); 236 VisitDirectory(base, ext, root);
219} 237}
220 238
221RomFSBuildContext::~RomFSBuildContext() = default; 239RomFSBuildContext::~RomFSBuildContext() = default;
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
index b0c3c123b..a62502193 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.h
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -27,7 +27,6 @@
27#include <map> 27#include <map>
28#include <memory> 28#include <memory>
29#include <string> 29#include <string>
30#include <boost/detail/container_fwd.hpp>
31#include "common/common_types.h" 30#include "common/common_types.h"
32#include "core/file_sys/vfs.h" 31#include "core/file_sys/vfs.h"
33 32
@@ -40,7 +39,7 @@ struct RomFSFileEntry;
40 39
41class RomFSBuildContext { 40class RomFSBuildContext {
42public: 41public:
43 explicit RomFSBuildContext(VirtualDir base); 42 explicit RomFSBuildContext(VirtualDir base, VirtualDir ext = nullptr);
44 ~RomFSBuildContext(); 43 ~RomFSBuildContext();
45 44
46 // This finalizes the context. 45 // This finalizes the context.
@@ -48,6 +47,7 @@ public:
48 47
49private: 48private:
50 VirtualDir base; 49 VirtualDir base;
50 VirtualDir ext;
51 std::shared_ptr<RomFSBuildDirectoryContext> root; 51 std::shared_ptr<RomFSBuildDirectoryContext> root;
52 std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories; 52 std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
53 std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files; 53 std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
@@ -59,7 +59,8 @@ private:
59 u64 file_hash_table_size = 0; 59 u64 file_hash_table_size = 0;
60 u64 file_partition_size = 0; 60 u64 file_partition_size = 0;
61 61
62 void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent); 62 void VisitDirectory(VirtualDir filesys, VirtualDir ext,
63 std::shared_ptr<RomFSBuildDirectoryContext> parent);
63 64
64 bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, 65 bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
65 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx); 66 std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index df933ee36..485c4913a 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -2,7 +2,15 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h" 5#include <algorithm>
6#include <cstring>
7#include <map>
8#include <sstream>
9#include <string>
10#include <utility>
11
12#include "common/hex_util.h"
13#include "common/logging/log.h"
6#include "common/swap.h" 14#include "common/swap.h"
7#include "core/file_sys/ips_layer.h" 15#include "core/file_sys/ips_layer.h"
8#include "core/file_sys/vfs_vector.h" 16#include "core/file_sys/vfs_vector.h"
@@ -15,16 +23,48 @@ enum class IPSFileType {
15 Error, 23 Error,
16}; 24};
17 25
26constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{
27 {"\\a", "\a"},
28 {"\\b", "\b"},
29 {"\\f", "\f"},
30 {"\\n", "\n"},
31 {"\\r", "\r"},
32 {"\\t", "\t"},
33 {"\\v", "\v"},
34 {"\\\\", "\\"},
35 {"\\\'", "\'"},
36 {"\\\"", "\""},
37 {"\\\?", "\?"},
38}};
39
18static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { 40static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
19 if (magic.size() != 5) 41 if (magic.size() != 5) {
20 return IPSFileType::Error; 42 return IPSFileType::Error;
21 if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) 43 }
44
45 constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
46 if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) {
22 return IPSFileType::IPS; 47 return IPSFileType::IPS;
23 if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) 48 }
49
50 constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
51 if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) {
24 return IPSFileType::IPS32; 52 return IPSFileType::IPS32;
53 }
54
25 return IPSFileType::Error; 55 return IPSFileType::Error;
26} 56}
27 57
58static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
59 constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
60 if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
61 return true;
62 }
63
64 constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
65 return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin());
66}
67
28VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { 68VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
29 if (in == nullptr || ips == nullptr) 69 if (in == nullptr || ips == nullptr)
30 return nullptr; 70 return nullptr;
@@ -39,8 +79,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
39 u64 offset = 5; // After header 79 u64 offset = 5; // After header
40 while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { 80 while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
41 offset += temp.size(); 81 offset += temp.size();
42 if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || 82 if (IsEOF(type, temp)) {
43 type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) {
44 break; 83 break;
45 } 84 }
46 85
@@ -60,29 +99,240 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
60 u16 rle_size{}; 99 u16 rle_size{};
61 if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) 100 if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
62 return nullptr; 101 return nullptr;
63 rle_size = Common::swap16(data_size); 102 rle_size = Common::swap16(rle_size);
64 offset += sizeof(u16); 103 offset += sizeof(u16);
65 104
66 const auto data = ips->ReadByte(offset++); 105 const auto data = ips->ReadByte(offset++);
67 if (data == boost::none) 106 if (!data)
68 return nullptr; 107 return nullptr;
69 108
70 if (real_offset + rle_size > in_data.size()) 109 if (real_offset + rle_size > in_data.size())
71 rle_size = in_data.size() - real_offset; 110 rle_size = static_cast<u16>(in_data.size() - real_offset);
72 std::memset(in_data.data() + real_offset, data.get(), rle_size); 111 std::memset(in_data.data() + real_offset, *data, rle_size);
73 } else { // Standard Patch 112 } else { // Standard Patch
74 auto read = data_size; 113 auto read = data_size;
75 if (real_offset + read > in_data.size()) 114 if (real_offset + read > in_data.size())
76 read = in_data.size() - real_offset; 115 read = static_cast<u16>(in_data.size() - real_offset);
77 if (ips->Read(in_data.data() + real_offset, read, offset) != data_size) 116 if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
78 return nullptr; 117 return nullptr;
79 offset += data_size; 118 offset += data_size;
80 } 119 }
81 } 120 }
82 121
83 if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) 122 if (!IsEOF(type, temp)) {
84 return nullptr; 123 return nullptr;
85 return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); 124 }
125
126 return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
127 in->GetContainingDirectory());
128}
129
130struct IPSwitchCompiler::IPSwitchPatch {
131 std::string name;
132 bool enabled;
133 std::map<u32, std::vector<u8>> records;
134};
135
136IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
137 Parse();
138}
139
140IPSwitchCompiler::~IPSwitchCompiler() = default;
141
142std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
143 return nso_build_id;
144}
145
146bool IPSwitchCompiler::IsValid() const {
147 return valid;
148}
149
150static bool StartsWith(std::string_view base, std::string_view check) {
151 return base.size() >= check.size() && base.substr(0, check.size()) == check;
152}
153
154static std::string EscapeStringSequences(std::string in) {
155 for (const auto& seq : ESCAPE_CHARACTER_MAP) {
156 for (auto index = in.find(seq.first); index != std::string::npos;
157 index = in.find(seq.first, index)) {
158 in.replace(index, std::strlen(seq.first), seq.second);
159 index += std::strlen(seq.second);
160 }
161 }
162
163 return in;
164}
165
166void IPSwitchCompiler::ParseFlag(const std::string& line) {
167 if (StartsWith(line, "@flag offset_shift ")) {
168 // Offset Shift Flag
169 offset_shift = std::stoll(line.substr(19), nullptr, 0);
170 } else if (StartsWith(line, "@little-endian")) {
171 // Set values to read as little endian
172 is_little_endian = true;
173 } else if (StartsWith(line, "@big-endian")) {
174 // Set values to read as big endian
175 is_little_endian = false;
176 } else if (StartsWith(line, "@flag print_values")) {
177 // Force printing of applied values
178 print_values = true;
179 }
180}
181
182void IPSwitchCompiler::Parse() {
183 const auto bytes = patch_text->ReadAllBytes();
184 std::stringstream s;
185 s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
186
187 std::vector<std::string> lines;
188 std::string stream_line;
189 while (std::getline(s, stream_line)) {
190 // Remove a trailing \r
191 if (!stream_line.empty() && stream_line.back() == '\r')
192 stream_line.pop_back();
193 lines.push_back(std::move(stream_line));
194 }
195
196 for (std::size_t i = 0; i < lines.size(); ++i) {
197 auto line = lines[i];
198
199 // Remove midline comments
200 std::size_t comment_index = std::string::npos;
201 bool within_string = false;
202 for (std::size_t k = 0; k < line.size(); ++k) {
203 if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
204 within_string = !within_string;
205 } else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
206 comment_index = k;
207 break;
208 }
209 }
210
211 if (!StartsWith(line, "//") && comment_index != std::string::npos) {
212 last_comment = line.substr(comment_index + 2);
213 line = line.substr(0, comment_index);
214 }
215
216 if (StartsWith(line, "@stop")) {
217 // Force stop
218 break;
219 } else if (StartsWith(line, "@nsobid-")) {
220 // NSO Build ID Specifier
221 auto raw_build_id = line.substr(8);
222 if (raw_build_id.size() != 0x40)
223 raw_build_id.resize(0x40, '0');
224 nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
225 } else if (StartsWith(line, "#")) {
226 // Mandatory Comment
227 LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
228 patch_text->GetName(), line.substr(1));
229 } else if (StartsWith(line, "//")) {
230 // Normal Comment
231 last_comment = line.substr(2);
232 if (last_comment.find_first_not_of(' ') == std::string::npos)
233 continue;
234 if (last_comment.find_first_not_of(' ') != 0)
235 last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
236 } else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
237 // Start of patch
238 const auto enabled = StartsWith(line, "@enabled");
239 if (i == 0)
240 return;
241 LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})",
242 patch_text->GetName(), last_comment, line.substr(1));
243
244 IPSwitchPatch patch{last_comment, enabled, {}};
245
246 // Read rest of patch
247 while (true) {
248 if (i + 1 >= lines.size())
249 break;
250 const auto patch_line = lines[++i];
251
252 // Start of new patch
253 if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
254 --i;
255 break;
256 }
257
258 // Check for a flag
259 if (StartsWith(patch_line, "@")) {
260 ParseFlag(patch_line);
261 continue;
262 }
263
264 // 11 - 8 hex digit offset + space + minimum two digit overwrite val
265 if (patch_line.length() < 11)
266 break;
267 auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16);
268 offset += static_cast<unsigned long>(offset_shift);
269
270 std::vector<u8> replace;
271 // 9 - first char of replacement val
272 if (patch_line[9] == '\"') {
273 // string replacement
274 auto end_index = patch_line.find('\"', 10);
275 if (end_index == std::string::npos || end_index < 10)
276 return;
277 while (patch_line[end_index - 1] == '\\') {
278 end_index = patch_line.find('\"', end_index + 1);
279 if (end_index == std::string::npos || end_index < 10)
280 return;
281 }
282
283 auto value = patch_line.substr(10, end_index - 10);
284 value = EscapeStringSequences(value);
285 replace.reserve(value.size());
286 std::copy(value.begin(), value.end(), std::back_inserter(replace));
287 } else {
288 // hex replacement
289 const auto value = patch_line.substr(9);
290 replace.reserve(value.size() / 2);
291 replace = Common::HexStringToVector(value, is_little_endian);
292 }
293
294 if (print_values) {
295 LOG_INFO(Loader,
296 "[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
297 "with byte string '{}'",
298 patch_text->GetName(), offset, Common::HexVectorToString(replace));
299 }
300
301 patch.records.insert_or_assign(offset, std::move(replace));
302 }
303
304 patches.push_back(std::move(patch));
305 } else if (StartsWith(line, "@")) {
306 ParseFlag(line);
307 }
308 }
309
310 valid = true;
311}
312
313VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
314 if (in == nullptr || !valid)
315 return nullptr;
316
317 auto in_data = in->ReadAllBytes();
318
319 for (const auto& patch : patches) {
320 if (!patch.enabled)
321 continue;
322
323 for (const auto& record : patch.records) {
324 if (record.first >= in_data.size())
325 continue;
326 auto replace_size = record.second.size();
327 if (record.first + replace_size > in_data.size())
328 replace_size = in_data.size() - record.first;
329 for (std::size_t i = 0; i < replace_size; ++i)
330 in_data[i + record.first] = record.second[i];
331 }
332 }
333
334 return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
335 in->GetContainingDirectory());
86} 336}
87 337
88} // namespace FileSys 338} // namespace FileSys
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h
index 81c163494..450b2f71e 100644
--- a/src/core/file_sys/ips_layer.h
+++ b/src/core/file_sys/ips_layer.h
@@ -4,12 +4,41 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <memory> 8#include <memory>
9#include <vector>
8 10
11#include "common/common_types.h"
9#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
10 13
11namespace FileSys { 14namespace FileSys {
12 15
13VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); 16VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips);
14 17
18class IPSwitchCompiler {
19public:
20 explicit IPSwitchCompiler(VirtualFile patch_text);
21 ~IPSwitchCompiler();
22
23 std::array<u8, 0x20> GetBuildID() const;
24 bool IsValid() const;
25 VirtualFile Apply(const VirtualFile& in) const;
26
27private:
28 struct IPSwitchPatch;
29
30 void ParseFlag(const std::string& flag);
31 void Parse();
32
33 bool valid = false;
34
35 VirtualFile patch_text;
36 std::vector<IPSwitchPatch> patches;
37 std::array<u8, 0x20> nso_build_id{};
38 bool is_little_endian = false;
39 s64 offset_shift = 0;
40 bool print_values = false;
41 std::string last_comment = "";
42};
43
15} // namespace FileSys 44} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 5791c76ff..a5259a593 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
83} 83}
84 84
85std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { 85std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
86 return pfs_dirs; 86 return {};
87} 87}
88 88
89std::string PartitionFilesystem::GetName() const { 89std::string PartitionFilesystem::GetName() const {
@@ -103,18 +103,4 @@ void PartitionFilesystem::PrintDebugInfo() const {
103 pfs_files[i]->GetName(), pfs_files[i]->GetSize()); 103 pfs_files[i]->GetName(), pfs_files[i]->GetSize());
104 } 104 }
105} 105}
106
107bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
108 const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
109 if (iter == pfs_files.end())
110 return false;
111
112 const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
113 pfs_files[offset] = std::move(pfs_files.back());
114 pfs_files.pop_back();
115
116 pfs_dirs.emplace_back(std::move(dir));
117
118 return true;
119}
120} // namespace FileSys 106} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index 739c63a7f..248fdfdeb 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -35,9 +35,6 @@ public:
35 std::shared_ptr<VfsDirectory> GetParentDirectory() const override; 35 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
36 void PrintDebugInfo() const; 36 void PrintDebugInfo() const;
37 37
38protected:
39 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
40
41private: 38private:
42 struct Header { 39 struct Header {
43 u32_le magic; 40 u32_le magic;
@@ -84,7 +81,6 @@ private:
84 std::size_t content_offset = 0; 81 std::size_t content_offset = 0;
85 82
86 std::vector<VirtualFile> pfs_files; 83 std::vector<VirtualFile> pfs_files;
87 std::vector<VirtualDir> pfs_dirs;
88}; 84};
89 85
90} // namespace FileSys 86} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 539698f6e..0c1156989 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -61,39 +61,49 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
61 // Game Updates 61 // Game Updates
62 const auto update_tid = GetUpdateTitleID(title_id); 62 const auto update_tid = GetUpdateTitleID(title_id);
63 const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); 63 const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
64 if (update != nullptr) { 64
65 if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && 65 if (update != nullptr && update->GetExeFS() != nullptr &&
66 update->GetExeFS() != nullptr) { 66 update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
67 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", 67 LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
68 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); 68 FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
69 exefs = update->GetExeFS(); 69 exefs = update->GetExeFS();
70 }
71 } 70 }
72 71
73 return exefs; 72 return exefs;
74} 73}
75 74
76static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs, 75static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
77 const std::string& build_id) { 76 const std::string& build_id) {
78 std::vector<VirtualFile> ips; 77 std::vector<VirtualFile> out;
79 ips.reserve(patch_dirs.size()); 78 out.reserve(patch_dirs.size());
80 for (const auto& subdir : patch_dirs) { 79 for (const auto& subdir : patch_dirs) {
81 auto exefs_dir = subdir->GetSubdirectory("exefs"); 80 auto exefs_dir = subdir->GetSubdirectory("exefs");
82 if (exefs_dir != nullptr) { 81 if (exefs_dir != nullptr) {
83 for (const auto& file : exefs_dir->GetFiles()) { 82 for (const auto& file : exefs_dir->GetFiles()) {
84 if (file->GetExtension() != "ips") 83 if (file->GetExtension() == "ips") {
85 continue; 84 auto name = file->GetName();
86 auto name = file->GetName(); 85 const auto p1 = name.substr(0, name.find('.'));
87 const auto p1 = name.substr(0, name.find('.')); 86 const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
88 const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); 87
89 88 if (build_id == this_build_id)
90 if (build_id == this_build_id) 89 out.push_back(file);
91 ips.push_back(file); 90 } else if (file->GetExtension() == "pchtxt") {
91 IPSwitchCompiler compiler{file};
92 if (!compiler.IsValid())
93 continue;
94
95 auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
96 this_build_id =
97 this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
98
99 if (build_id == this_build_id)
100 out.push_back(file);
101 }
92 } 102 }
93 } 103 }
94 } 104 }
95 105
96 return ips; 106 return out;
97} 107}
98 108
99std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { 109std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
@@ -115,15 +125,24 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
115 auto patch_dirs = load_dir->GetSubdirectories(); 125 auto patch_dirs = load_dir->GetSubdirectories();
116 std::sort(patch_dirs.begin(), patch_dirs.end(), 126 std::sort(patch_dirs.begin(), patch_dirs.end(),
117 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 127 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
118 const auto ips = CollectIPSPatches(patch_dirs, build_id); 128 const auto patches = CollectPatches(patch_dirs, build_id);
119 129
120 auto out = nso; 130 auto out = nso;
121 for (const auto& ips_file : ips) { 131 for (const auto& patch_file : patches) {
122 LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"", 132 if (patch_file->GetExtension() == "ips") {
123 ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); 133 LOG_INFO(Loader, " - Applying IPS patch from mod \"{}\"",
124 const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); 134 patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
125 if (patched != nullptr) 135 const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), patch_file);
126 out = patched->ReadAllBytes(); 136 if (patched != nullptr)
137 out = patched->ReadAllBytes();
138 } else if (patch_file->GetExtension() == "pchtxt") {
139 LOG_INFO(Loader, " - Applying IPSwitch patch from mod \"{}\"",
140 patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
141 const IPSwitchCompiler compiler{patch_file};
142 const auto patched = compiler.Apply(std::make_shared<VectorVfsFile>(out));
143 if (patched != nullptr)
144 out = patched->ReadAllBytes();
145 }
127 } 146 }
128 147
129 if (out.size() < 0x100) 148 if (out.size() < 0x100)
@@ -143,12 +162,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
143 std::sort(patch_dirs.begin(), patch_dirs.end(), 162 std::sort(patch_dirs.begin(), patch_dirs.end(),
144 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 163 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
145 164
146 return !CollectIPSPatches(patch_dirs, build_id).empty(); 165 return !CollectPatches(patch_dirs, build_id).empty();
147} 166}
148 167
149static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 168static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
150 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 169 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
151 if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { 170 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
171 load_dir == nullptr || load_dir->GetSize() <= 0) {
152 return; 172 return;
153 } 173 }
154 174
@@ -162,11 +182,17 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
162 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 182 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
163 183
164 std::vector<VirtualDir> layers; 184 std::vector<VirtualDir> layers;
185 std::vector<VirtualDir> layers_ext;
165 layers.reserve(patch_dirs.size() + 1); 186 layers.reserve(patch_dirs.size() + 1);
187 layers_ext.reserve(patch_dirs.size() + 1);
166 for (const auto& subdir : patch_dirs) { 188 for (const auto& subdir : patch_dirs) {
167 auto romfs_dir = subdir->GetSubdirectory("romfs"); 189 auto romfs_dir = subdir->GetSubdirectory("romfs");
168 if (romfs_dir != nullptr) 190 if (romfs_dir != nullptr)
169 layers.push_back(std::move(romfs_dir)); 191 layers.push_back(std::move(romfs_dir));
192
193 auto ext_dir = subdir->GetSubdirectory("romfs_ext");
194 if (ext_dir != nullptr)
195 layers_ext.push_back(std::move(ext_dir));
170 } 196 }
171 layers.push_back(std::move(extracted)); 197 layers.push_back(std::move(extracted));
172 198
@@ -175,7 +201,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
175 return; 201 return;
176 } 202 }
177 203
178 auto packed = CreateRomFS(std::move(layered)); 204 auto layered_ext = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers_ext));
205
206 auto packed = CreateRomFS(std::move(layered), std::move(layered_ext));
179 if (packed == nullptr) { 207 if (packed == nullptr) {
180 return; 208 return;
181 } 209 }
@@ -184,10 +212,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
184 romfs = std::move(packed); 212 romfs = std::move(packed);
185} 213}
186 214
187VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, 215VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
188 ContentRecordType type) const { 216 VirtualFile update_raw) const {
189 LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, 217 const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
190 static_cast<u8>(type)); 218 title_id, static_cast<u8>(type))
219 .c_str();
220
221 if (type == ContentRecordType::Program || type == ContentRecordType::Data)
222 LOG_INFO(Loader, log_string);
223 else
224 LOG_DEBUG(Loader, log_string);
191 225
192 if (romfs == nullptr) 226 if (romfs == nullptr)
193 return romfs; 227 return romfs;
@@ -202,7 +236,14 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
202 if (new_nca->GetStatus() == Loader::ResultStatus::Success && 236 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
203 new_nca->GetRomFS() != nullptr) { 237 new_nca->GetRomFS() != nullptr) {
204 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", 238 LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
205 FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); 239 FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
240 romfs = new_nca->GetRomFS();
241 }
242 } else if (update_raw != nullptr) {
243 const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
244 if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
245 new_nca->GetRomFS() != nullptr) {
246 LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully");
206 romfs = new_nca->GetRomFS(); 247 romfs = new_nca->GetRomFS();
207 } 248 }
208 } 249 }
@@ -224,7 +265,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
224 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); 265 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
225} 266}
226 267
227std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames() const { 268std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
269 VirtualFile update_raw) const {
228 std::map<std::string, std::string, std::less<>> out; 270 std::map<std::string, std::string, std::less<>> out;
229 const auto installed = Service::FileSystem::GetUnionContents(); 271 const auto installed = Service::FileSystem::GetUnionContents();
230 272
@@ -238,13 +280,14 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
238 } else { 280 } else {
239 if (installed->HasEntry(update_tid, ContentRecordType::Program)) { 281 if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
240 const auto meta_ver = installed->GetEntryVersion(update_tid); 282 const auto meta_ver = installed->GetEntryVersion(update_tid);
241 if (meta_ver == boost::none || meta_ver.get() == 0) { 283 if (meta_ver.value_or(0) == 0) {
242 out.insert_or_assign("Update", ""); 284 out.insert_or_assign("Update", "");
243 } else { 285 } else {
244 out.insert_or_assign( 286 out.insert_or_assign(
245 "Update", 287 "Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
246 FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
247 } 288 }
289 } else if (update_raw != nullptr) {
290 out.insert_or_assign("Update", "PACKED");
248 } 291 }
249 } 292 }
250 293
@@ -253,8 +296,24 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
253 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 296 if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
254 for (const auto& mod : mod_dir->GetSubdirectories()) { 297 for (const auto& mod : mod_dir->GetSubdirectories()) {
255 std::string types; 298 std::string types;
256 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs"))) 299
257 AppendCommaIfNotEmpty(types, "IPS"); 300 const auto exefs_dir = mod->GetSubdirectory("exefs");
301 if (IsDirValidAndNonEmpty(exefs_dir)) {
302 bool ips = false;
303 bool ipswitch = false;
304
305 for (const auto& file : exefs_dir->GetFiles()) {
306 if (file->GetExtension() == "ips")
307 ips = true;
308 else if (file->GetExtension() == "pchtxt")
309 ipswitch = true;
310 }
311
312 if (ips)
313 AppendCommaIfNotEmpty(types, "IPS");
314 if (ipswitch)
315 AppendCommaIfNotEmpty(types, "IPSwitch");
316 }
258 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) 317 if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
259 AppendCommaIfNotEmpty(types, "LayeredFS"); 318 AppendCommaIfNotEmpty(types, "LayeredFS");
260 319
@@ -291,23 +350,22 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
291 return out; 350 return out;
292} 351}
293 352
294std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 353std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
295 const auto& installed{Service::FileSystem::GetUnionContents()}; 354 const auto installed{Service::FileSystem::GetUnionContents()};
296 355
297 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); 356 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
298 if (base_control_nca == nullptr) 357 if (base_control_nca == nullptr)
299 return {}; 358 return {};
300 359
301 return ParseControlNCA(base_control_nca); 360 return ParseControlNCA(*base_control_nca);
302} 361}
303 362
304std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA( 363std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const {
305 const std::shared_ptr<NCA>& nca) const { 364 const auto base_romfs = nca.GetRomFS();
306 const auto base_romfs = nca->GetRomFS();
307 if (base_romfs == nullptr) 365 if (base_romfs == nullptr)
308 return {}; 366 return {};
309 367
310 const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control); 368 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
311 if (romfs == nullptr) 369 if (romfs == nullptr)
312 return {}; 370 return {};
313 371
@@ -319,7 +377,7 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
319 if (nacp_file == nullptr) 377 if (nacp_file == nullptr)
320 nacp_file = extracted->GetFile("Control.nacp"); 378 nacp_file = extracted->GetFile("Control.nacp");
321 379
322 const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file); 380 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
323 381
324 VirtualFile icon_file; 382 VirtualFile icon_file;
325 for (const auto& language : FileSys::LANGUAGE_NAMES) { 383 for (const auto& language : FileSys::LANGUAGE_NAMES) {
@@ -328,6 +386,6 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
328 break; 386 break;
329 } 387 }
330 388
331 return {nacp, icon_file}; 389 return {std::move(nacp), icon_file};
332} 390}
333} // namespace FileSys 391} // namespace FileSys
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 6a864ec43..7d168837f 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -36,6 +36,7 @@ public:
36 36
37 // Currently tracked NSO patches: 37 // Currently tracked NSO patches:
38 // - IPS 38 // - IPS
39 // - IPSwitch
39 std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; 40 std::vector<u8> PatchNSO(const std::vector<u8>& nso) const;
40 41
41 // Checks to see if PatchNSO() will have any effect given the NSO's build ID. 42 // Checks to see if PatchNSO() will have any effect given the NSO's build ID.
@@ -46,19 +47,20 @@ public:
46 // - Game Updates 47 // - Game Updates
47 // - LayeredFS 48 // - LayeredFS
48 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, 49 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
49 ContentRecordType type = ContentRecordType::Program) const; 50 ContentRecordType type = ContentRecordType::Program,
51 VirtualFile update_raw = nullptr) const;
50 52
51 // Returns a vector of pairs between patch names and patch versions. 53 // Returns a vector of pairs between patch names and patch versions.
52 // i.e. Update 3.2.2 will return {"Update", "3.2.2"} 54 // i.e. Update 3.2.2 will return {"Update", "3.2.2"}
53 std::map<std::string, std::string, std::less<>> GetPatchVersionNames() const; 55 std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
56 VirtualFile update_raw = nullptr) const;
54 57
55 // Given title_id of the program, attempts to get the control data of the update and parse it, 58 // Given title_id of the program, attempts to get the control data of the update and parse it,
56 // falling back to the base control data. 59 // falling back to the base control data.
57 std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const; 60 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
58 61
59 // Version of GetControlMetadata that takes an arbitrary NCA 62 // Version of GetControlMetadata that takes an arbitrary NCA
60 std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA( 63 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
61 const std::shared_ptr<NCA>& nca) const;
62 64
63private: 65private:
64 u64 title_id; 66 u64 title_id;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e9b040689..96302a241 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <regex> 6#include <regex>
6#include <mbedtls/sha256.h> 7#include <mbedtls/sha256.h>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -30,6 +31,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
30 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); 31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
31} 32}
32 33
34bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36}
37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
39 return !operator==(lhs, rhs);
40}
41
33static bool FollowsTwoDigitDirFormat(std::string_view name) { 42static bool FollowsTwoDigitDirFormat(std::string_view name) {
34 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | 43 static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
35 std::regex_constants::icase); 44 std::regex_constants::icase);
@@ -150,28 +159,28 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
150 return file; 159 return file;
151} 160}
152 161
153static boost::optional<NcaID> CheckMapForContentRecord( 162static std::optional<NcaID> CheckMapForContentRecord(
154 const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { 163 const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
155 if (map.find(title_id) == map.end()) 164 if (map.find(title_id) == map.end())
156 return boost::none; 165 return {};
157 166
158 const auto& cnmt = map.at(title_id); 167 const auto& cnmt = map.at(title_id);
159 168
160 const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(), 169 const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
161 [type](const ContentRecord& rec) { return rec.type == type; }); 170 [type](const ContentRecord& rec) { return rec.type == type; });
162 if (iter == cnmt.GetContentRecords().end()) 171 if (iter == cnmt.GetContentRecords().end())
163 return boost::none; 172 return {};
164 173
165 return boost::make_optional(iter->nca_id); 174 return std::make_optional(iter->nca_id);
166} 175}
167 176
168boost::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id, 177std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
169 ContentRecordType type) const { 178 ContentRecordType type) const {
170 if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end()) 179 if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end())
171 return meta_id.at(title_id); 180 return meta_id.at(title_id);
172 181
173 const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type); 182 const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type);
174 if (res1 != boost::none) 183 if (res1)
175 return res1; 184 return res1;
176 return CheckMapForContentRecord(meta, title_id, type); 185 return CheckMapForContentRecord(meta, title_id, type);
177} 186}
@@ -274,17 +283,14 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
274 283
275VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 284VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
276 const auto id = GetNcaIDFromMetadata(title_id, type); 285 const auto id = GetNcaIDFromMetadata(title_id, type);
277 if (id == boost::none) 286 return id ? GetFileAtID(*id) : nullptr;
278 return nullptr;
279
280 return GetFileAtID(id.get());
281} 287}
282 288
283VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const { 289VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
284 return GetEntryUnparsed(entry.title_id, entry.type); 290 return GetEntryUnparsed(entry.title_id, entry.type);
285} 291}
286 292
287boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { 293std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
288 const auto meta_iter = meta.find(title_id); 294 const auto meta_iter = meta.find(title_id);
289 if (meta_iter != meta.end()) 295 if (meta_iter != meta.end())
290 return meta_iter->second.GetTitleVersion(); 296 return meta_iter->second.GetTitleVersion();
@@ -293,29 +299,26 @@ boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
293 if (yuzu_meta_iter != yuzu_meta.end()) 299 if (yuzu_meta_iter != yuzu_meta.end())
294 return yuzu_meta_iter->second.GetTitleVersion(); 300 return yuzu_meta_iter->second.GetTitleVersion();
295 301
296 return boost::none; 302 return {};
297} 303}
298 304
299VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { 305VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
300 const auto id = GetNcaIDFromMetadata(title_id, type); 306 const auto id = GetNcaIDFromMetadata(title_id, type);
301 if (id == boost::none) 307 return id ? parser(GetFileAtID(*id), *id) : nullptr;
302 return nullptr;
303
304 return parser(GetFileAtID(id.get()), id.get());
305} 308}
306 309
307VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const { 310VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
308 return GetEntryRaw(entry.title_id, entry.type); 311 return GetEntryRaw(entry.title_id, entry.type);
309} 312}
310 313
311std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 314std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
312 const auto raw = GetEntryRaw(title_id, type); 315 const auto raw = GetEntryRaw(title_id, type);
313 if (raw == nullptr) 316 if (raw == nullptr)
314 return nullptr; 317 return nullptr;
315 return std::make_shared<NCA>(raw); 318 return std::make_unique<NCA>(raw);
316} 319}
317 320
318std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 321std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
319 return GetEntry(entry.title_id, entry.type); 322 return GetEntry(entry.title_id, entry.type);
320} 323}
321 324
@@ -355,8 +358,8 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
355} 358}
356 359
357std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( 360std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
358 boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type, 361 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
359 boost::optional<u64> title_id) const { 362 std::optional<u64> title_id) const {
360 std::vector<RegisteredCacheEntry> out; 363 std::vector<RegisteredCacheEntry> out;
361 IterateAllMetadata<RegisteredCacheEntry>( 364 IterateAllMetadata<RegisteredCacheEntry>(
362 out, 365 out,
@@ -364,11 +367,11 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
364 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 367 return RegisteredCacheEntry{c.GetTitleID(), r.type};
365 }, 368 },
366 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 369 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
367 if (title_type != boost::none && title_type.get() != c.GetType()) 370 if (title_type && *title_type != c.GetType())
368 return false; 371 return false;
369 if (record_type != boost::none && record_type.get() != r.type) 372 if (record_type && *record_type != r.type)
370 return false; 373 return false;
371 if (title_id != boost::none && title_id.get() != c.GetTitleID()) 374 if (title_id && *title_id != c.GetTitleID())
372 return false; 375 return false;
373 return true; 376 return true;
374 }); 377 });
@@ -450,7 +453,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType
450 453
451InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, 454InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
452 bool overwrite_if_exists, 455 bool overwrite_if_exists,
453 boost::optional<NcaID> override_id) { 456 std::optional<NcaID> override_id) {
454 const auto in = nca->GetBaseFile(); 457 const auto in = nca->GetBaseFile();
455 Core::Crypto::SHA256Hash hash{}; 458 Core::Crypto::SHA256Hash hash{};
456 459
@@ -459,12 +462,12 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
459 // game is massive), we're going to cheat and only hash the first MB of the NCA. 462 // game is massive), we're going to cheat and only hash the first MB of the NCA.
460 // Also, for XCIs the NcaID matters, so if the override id isn't none, use that. 463 // Also, for XCIs the NcaID matters, so if the override id isn't none, use that.
461 NcaID id{}; 464 NcaID id{};
462 if (override_id == boost::none) { 465 if (override_id) {
466 id = *override_id;
467 } else {
463 const auto& data = in->ReadBytes(0x100000); 468 const auto& data = in->ReadBytes(0x100000);
464 mbedtls_sha256(data.data(), data.size(), hash.data(), 0); 469 mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
465 memcpy(id.data(), hash.data(), 16); 470 memcpy(id.data(), hash.data(), 16);
466 } else {
467 id = override_id.get();
468 } 471 }
469 472
470 std::string path = GetRelativePathFromNcaID(id, false, true); 473 std::string path = GetRelativePathFromNcaID(id, false, true);
@@ -516,7 +519,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
516 }) != yuzu_meta.end(); 519 }) != yuzu_meta.end();
517} 520}
518 521
519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) 522RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
520 : caches(std::move(caches)) {} 523 : caches(std::move(caches)) {}
521 524
522void RegisteredCacheUnion::Refresh() { 525void RegisteredCacheUnion::Refresh() {
@@ -534,14 +537,14 @@ bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
534 return HasEntry(entry.title_id, entry.type); 537 return HasEntry(entry.title_id, entry.type);
535} 538}
536 539
537boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { 540std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
538 for (const auto& c : caches) { 541 for (const auto& c : caches) {
539 const auto res = c->GetEntryVersion(title_id); 542 const auto res = c->GetEntryVersion(title_id);
540 if (res != boost::none) 543 if (res)
541 return res; 544 return res;
542 } 545 }
543 546
544 return boost::none; 547 return {};
545} 548}
546 549
547VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 550VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
@@ -572,14 +575,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
572 return GetEntryRaw(entry.title_id, entry.type); 575 return GetEntryRaw(entry.title_id, entry.type);
573} 576}
574 577
575std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 578std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
576 const auto raw = GetEntryRaw(title_id, type); 579 const auto raw = GetEntryRaw(title_id, type);
577 if (raw == nullptr) 580 if (raw == nullptr)
578 return nullptr; 581 return nullptr;
579 return std::make_shared<NCA>(raw); 582 return std::make_unique<NCA>(raw);
580} 583}
581 584
582std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 585std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
583 return GetEntry(entry.title_id, entry.type); 586 return GetEntry(entry.title_id, entry.type);
584} 587}
585 588
@@ -593,12 +596,15 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
593 }, 596 },
594 [](const CNMT& c, const ContentRecord& r) { return true; }); 597 [](const CNMT& c, const ContentRecord& r) { return true; });
595 } 598 }
599
600 std::sort(out.begin(), out.end());
601 out.erase(std::unique(out.begin(), out.end()), out.end());
596 return out; 602 return out;
597} 603}
598 604
599std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( 605std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
600 boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type, 606 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
601 boost::optional<u64> title_id) const { 607 std::optional<u64> title_id) const {
602 std::vector<RegisteredCacheEntry> out; 608 std::vector<RegisteredCacheEntry> out;
603 for (const auto& c : caches) { 609 for (const auto& c : caches) {
604 c->IterateAllMetadata<RegisteredCacheEntry>( 610 c->IterateAllMetadata<RegisteredCacheEntry>(
@@ -607,15 +613,18 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
607 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 613 return RegisteredCacheEntry{c.GetTitleID(), r.type};
608 }, 614 },
609 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 615 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
610 if (title_type != boost::none && title_type.get() != c.GetType()) 616 if (title_type && *title_type != c.GetType())
611 return false; 617 return false;
612 if (record_type != boost::none && record_type.get() != r.type) 618 if (record_type && *record_type != r.type)
613 return false; 619 return false;
614 if (title_id != boost::none && title_id.get() != c.GetTitleID()) 620 if (title_id && *title_id != c.GetTitleID())
615 return false; 621 return false;
616 return true; 622 return true;
617 }); 623 });
618 } 624 }
625
626 std::sort(out.begin(), out.end());
627 out.erase(std::unique(out.begin(), out.end()), out.end());
619 return out; 628 return out;
620} 629}
621} // namespace FileSys 630} // namespace FileSys
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index c0cd59fc5..6cfb16017 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -50,6 +50,10 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
50// boost flat_map requires operator< for O(log(n)) lookups. 50// boost flat_map requires operator< for O(log(n)) lookups.
51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
52 52
53// std unique requires operator== to identify duplicates.
54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
55bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
56
53/* 57/*
54 * A class that catalogues NCAs in the registered directory structure. 58 * A class that catalogues NCAs in the registered directory structure.
55 * Nintendo's registered format follows this structure: 59 * Nintendo's registered format follows this structure:
@@ -60,8 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
60 * | 00 64 * | 00
61 * | 01 <- Actual content split along 4GB boundaries. (optional) 65 * | 01 <- Actual content split along 4GB boundaries. (optional)
62 * 66 *
63 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient when 67 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
64 * 4GB splitting can be ignored.) 68 * when 4GB splitting can be ignored.)
65 */ 69 */
66class RegisteredCache { 70class RegisteredCache {
67 friend class RegisteredCacheUnion; 71 friend class RegisteredCacheUnion;
@@ -80,7 +84,7 @@ public:
80 bool HasEntry(u64 title_id, ContentRecordType type) const; 84 bool HasEntry(u64 title_id, ContentRecordType type) const;
81 bool HasEntry(RegisteredCacheEntry entry) const; 85 bool HasEntry(RegisteredCacheEntry entry) const;
82 86
83 boost::optional<u32> GetEntryVersion(u64 title_id) const; 87 std::optional<u32> GetEntryVersion(u64 title_id) const;
84 88
85 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 89 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
86 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; 90 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -88,15 +92,14 @@ public:
88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 92 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 93 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
90 94
91 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 95 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
92 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 96 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
93 97
94 std::vector<RegisteredCacheEntry> ListEntries() const; 98 std::vector<RegisteredCacheEntry> ListEntries() const;
95 // If a parameter is not boost::none, it will be filtered for from all entries. 99 // If a parameter is not std::nullopt, it will be filtered for from all entries.
96 std::vector<RegisteredCacheEntry> ListEntriesFilter( 100 std::vector<RegisteredCacheEntry> ListEntriesFilter(
97 boost::optional<TitleType> title_type = boost::none, 101 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
98 boost::optional<ContentRecordType> record_type = boost::none, 102 std::optional<u64> title_id = {}) const;
99 boost::optional<u64> title_id = boost::none) const;
100 103
101 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 104 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
102 // there is a meta NCA and all of them are accessible. 105 // there is a meta NCA and all of them are accessible.
@@ -121,12 +124,11 @@ private:
121 std::vector<NcaID> AccumulateFiles() const; 124 std::vector<NcaID> AccumulateFiles() const;
122 void ProcessFiles(const std::vector<NcaID>& ids); 125 void ProcessFiles(const std::vector<NcaID>& ids);
123 void AccumulateYuzuMeta(); 126 void AccumulateYuzuMeta();
124 boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; 127 std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
125 VirtualFile GetFileAtID(NcaID id) const; 128 VirtualFile GetFileAtID(NcaID id) const;
126 VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const; 129 VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
127 InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, 130 InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
128 bool overwrite_if_exists, 131 bool overwrite_if_exists, std::optional<NcaID> override_id = {});
129 boost::optional<NcaID> override_id = boost::none);
130 bool RawInstallYuzuMeta(const CNMT& cnmt); 132 bool RawInstallYuzuMeta(const CNMT& cnmt);
131 133
132 VirtualDir dir; 134 VirtualDir dir;
@@ -142,14 +144,14 @@ private:
142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 144// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
143class RegisteredCacheUnion { 145class RegisteredCacheUnion {
144public: 146public:
145 explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); 147 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
146 148
147 void Refresh(); 149 void Refresh();
148 150
149 bool HasEntry(u64 title_id, ContentRecordType type) const; 151 bool HasEntry(u64 title_id, ContentRecordType type) const;
150 bool HasEntry(RegisteredCacheEntry entry) const; 152 bool HasEntry(RegisteredCacheEntry entry) const;
151 153
152 boost::optional<u32> GetEntryVersion(u64 title_id) const; 154 std::optional<u32> GetEntryVersion(u64 title_id) const;
153 155
154 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 156 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
155 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; 157 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -157,18 +159,17 @@ public:
157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 159 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 160 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
159 161
160 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 162 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
161 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 163 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162 164
163 std::vector<RegisteredCacheEntry> ListEntries() const; 165 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries. 166 // If a parameter is not std::nullopt, it will be filtered for from all entries.
165 std::vector<RegisteredCacheEntry> ListEntriesFilter( 167 std::vector<RegisteredCacheEntry> ListEntriesFilter(
166 boost::optional<TitleType> title_type = boost::none, 168 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
167 boost::optional<ContentRecordType> record_type = boost::none, 169 std::optional<u64> title_id = {}) const;
168 boost::optional<u64> title_id = boost::none) const;
169 170
170private: 171private:
171 std::vector<std::shared_ptr<RegisteredCache>> caches; 172 std::vector<RegisteredCache*> caches;
172}; 173};
173 174
174} // namespace FileSys 175} // namespace FileSys
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 5910f7046..81e1f66ac 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -129,11 +129,11 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
129 return out; 129 return out;
130} 130}
131 131
132VirtualFile CreateRomFS(VirtualDir dir) { 132VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
133 if (dir == nullptr) 133 if (dir == nullptr)
134 return nullptr; 134 return nullptr;
135 135
136 RomFSBuildContext ctx{dir}; 136 RomFSBuildContext ctx{dir, ext};
137 return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); 137 return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
138} 138}
139 139
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index ecd1eb725..0ec404731 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -44,6 +44,6 @@ VirtualDir ExtractRomFS(VirtualFile file,
44 44
45// Converts a VFS filesystem into a RomFS binary 45// Converts a VFS filesystem into a RomFS binary
46// Returns nullptr on failure 46// Returns nullptr on failure
47VirtualFile CreateRomFS(VirtualDir dir); 47VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext = nullptr);
48 48
49} // namespace FileSys 49} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 4994c2532..0b645b106 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -30,12 +30,17 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
30 30
31RomFSFactory::~RomFSFactory() = default; 31RomFSFactory::~RomFSFactory() = default;
32 32
33void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
34 this->update_raw = std::move(update_raw);
35}
36
33ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { 37ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
34 if (!updatable) 38 if (!updatable)
35 return MakeResult<VirtualFile>(file); 39 return MakeResult<VirtualFile>(file);
36 40
37 const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID()); 41 const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID());
38 return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset)); 42 return MakeResult<VirtualFile>(
43 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
39} 44}
40 45
41ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { 46ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 2cace8180..7724c0b23 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -32,11 +32,13 @@ public:
32 explicit RomFSFactory(Loader::AppLoader& app_loader); 32 explicit RomFSFactory(Loader::AppLoader& app_loader);
33 ~RomFSFactory(); 33 ~RomFSFactory();
34 34
35 void SetPackedUpdate(VirtualFile update_raw);
35 ResultVal<VirtualFile> OpenCurrentProcess(); 36 ResultVal<VirtualFile> OpenCurrentProcess();
36 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); 37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
37 38
38private: 39private:
39 VirtualFile file; 40 VirtualFile file;
41 VirtualFile update_raw;
40 bool updatable; 42 bool updatable;
41 u64 ivfc_offset; 43 u64 ivfc_offset;
42}; 44};
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 47f2ab9e0..ef1aaebbb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
51 meta.title_id); 51 meta.title_id);
52 } 52 }
53 53
54 if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
55 LOG_WARNING(Service_FS,
56 "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
57 "non-zero ({:016X}{:016X})",
58 meta.user_id[1], meta.user_id[0]);
59 }
60
54 std::string save_directory = 61 std::string save_directory =
55 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 62 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
56 63
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
92 case SaveDataSpaceId::NandUser: 99 case SaveDataSpaceId::NandUser:
93 out = "/user/"; 100 out = "/user/";
94 break; 101 break;
102 case SaveDataSpaceId::TemporaryStorage:
103 out = "/temp/";
104 break;
95 default: 105 default:
96 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); 106 ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
97 } 107 }
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
100 case SaveDataType::SystemSaveData: 110 case SaveDataType::SystemSaveData:
101 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); 111 return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
102 case SaveDataType::SaveData: 112 case SaveDataType::SaveData:
113 case SaveDataType::DeviceSaveData:
103 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 114 return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
104 title_id); 115 title_id);
105 case SaveDataType::TemporaryStorage: 116 case SaveDataType::TemporaryStorage:
106 return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], 117 return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
107 title_id); 118 title_id);
108 default: 119 default:
109 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); 120 ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d66a9c9a4..bd3a57058 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -10,10 +10,10 @@
10namespace FileSys { 10namespace FileSys {
11 11
12SDMCFactory::SDMCFactory(VirtualDir dir_) 12SDMCFactory::SDMCFactory(VirtualDir dir_)
13 : dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>( 13 : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
15 [](const VirtualFile& file, const NcaID& id) { 15 [](const VirtualFile& file, const NcaID& id) {
16 return std::make_shared<NAX>(file, id)->GetDecrypted(); 16 return NAX{file, id}.GetDecrypted();
17 })) {} 17 })) {}
18 18
19SDMCFactory::~SDMCFactory() = default; 19SDMCFactory::~SDMCFactory() = default;
@@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
22 return MakeResult<VirtualDir>(dir); 22 return MakeResult<VirtualDir>(dir);
23} 23}
24 24
25std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const { 25RegisteredCache* SDMCFactory::GetSDMCContents() const {
26 return contents; 26 return contents.get();
27} 27}
28 28
29} // namespace FileSys 29} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index ea12149de..42794ba5b 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,12 +19,12 @@ public:
19 ~SDMCFactory(); 19 ~SDMCFactory();
20 20
21 ResultVal<VirtualDir> Open(); 21 ResultVal<VirtualDir> Open();
22 std::shared_ptr<RegisteredCache> GetSDMCContents() const; 22 RegisteredCache* GetSDMCContents() const;
23 23
24private: 24private:
25 VirtualDir dir; 25 VirtualDir dir;
26 26
27 std::shared_ptr<RegisteredCache> contents; 27 std::unique_ptr<RegisteredCache> contents;
28}; 28};
29 29
30} // namespace FileSys 30} // namespace FileSys
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 09bf077cd..2aaba4179 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -205,10 +205,6 @@ VirtualDir NSP::GetParentDirectory() const {
205 return file->GetContainingDirectory(); 205 return file->GetContainingDirectory();
206} 206}
207 207
208bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
209 return false;
210}
211
212void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { 208void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
213 exefs = pfs; 209 exefs = pfs;
214 210
@@ -259,8 +255,11 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
259 auto next_nca = std::make_shared<NCA>(next_file); 255 auto next_nca = std::make_shared<NCA>(next_file);
260 if (next_nca->GetType() == NCAContentType::Program) 256 if (next_nca->GetType() == NCAContentType::Program)
261 program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); 257 program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
262 if (next_nca->GetStatus() == Loader::ResultStatus::Success) 258 if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
259 (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
260 (cnmt.GetTitleID() & 0x800) != 0)) {
263 ncas_title[rec.type] = std::move(next_nca); 261 ncas_title[rec.type] = std::move(next_nca);
262 }
264 } 263 }
265 264
266 break; 265 break;
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index da3dc5e9f..338080b7e 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -55,9 +55,6 @@ public:
55 55
56 VirtualDir GetParentDirectory() const override; 56 VirtualDir GetParentDirectory() const override;
57 57
58protected:
59 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
60
61private: 58private:
62 void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); 59 void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
63 void ReadNCAs(const std::vector<VirtualFile>& files); 60 void ReadNCAs(const std::vector<VirtualFile>& files);
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index bfe50da73..7b584de7f 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -167,13 +167,13 @@ std::string VfsFile::GetExtension() const {
167 167
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
169 169
170boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const { 170std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
171 u8 out{}; 171 u8 out{};
172 std::size_t size = Read(&out, 1, offset); 172 std::size_t size = Read(&out, 1, offset);
173 if (size == 1) 173 if (size == 1)
174 return out; 174 return out;
175 175
176 return boost::none; 176 return {};
177} 177}
178 178
179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { 179std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
@@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
472 std::vector<u8> temp(std::min(block_size, src->GetSize())); 472 std::vector<u8> temp(std::min(block_size, src->GetSize()));
473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) { 473 for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
474 const auto read = std::min(block_size, src->GetSize() - i); 474 const auto read = std::min(block_size, src->GetSize() - i);
475 const auto block = src->Read(temp.data(), read, i);
476 475
477 if (dest->Write(temp.data(), read, i) != read) 476 if (src->Read(temp.data(), read, i) != read) {
478 return false; 477 return false;
478 }
479
480 if (dest->Write(temp.data(), read, i) != read) {
481 return false;
482 }
479 } 483 }
480 484
481 return true; 485 return true;
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 270291631..002f99d4e 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -4,28 +4,22 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
7#include <map> 8#include <map>
8#include <memory> 9#include <memory>
10#include <optional>
9#include <string> 11#include <string>
10#include <string_view> 12#include <string_view>
11#include <type_traits> 13#include <type_traits>
12#include <vector> 14#include <vector>
13#include <boost/optional.hpp> 15
14#include "common/common_types.h" 16#include "common/common_types.h"
17#include "core/file_sys/vfs_types.h"
15 18
16namespace FileSys { 19namespace FileSys {
17 20
18class VfsDirectory;
19class VfsFile;
20class VfsFilesystem;
21
22enum class Mode : u32; 21enum class Mode : u32;
23 22
24// Convenience typedefs to use Vfs* interfaces
25using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
26using VirtualDir = std::shared_ptr<VfsDirectory>;
27using VirtualFile = std::shared_ptr<VfsFile>;
28
29// An enumeration representing what can be at the end of a path in a VfsFilesystem 23// An enumeration representing what can be at the end of a path in a VfsFilesystem
30enum class VfsEntryType { 24enum class VfsEntryType {
31 None, 25 None,
@@ -111,8 +105,8 @@ public:
111 // into file. Returns number of bytes successfully written. 105 // into file. Returns number of bytes successfully written.
112 virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; 106 virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
113 107
114 // Reads exactly one byte at the offset provided, returning boost::none on error. 108 // Reads exactly one byte at the offset provided, returning std::nullopt on error.
115 virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const; 109 virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
116 // Reads size bytes starting at offset in file into a vector. 110 // Reads size bytes starting at offset in file into a vector.
117 virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; 111 virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
118 // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), 112 // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
@@ -270,36 +264,8 @@ public:
270 // item name -> type. 264 // item name -> type.
271 virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; 265 virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
272 266
273 // Interprets the file with name file instead as a directory of type directory.
274 // The directory must have a constructor that takes a single argument of type
275 // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
276 // subdirectory in one call.
277 template <typename Directory>
278 bool InterpretAsDirectory(std::string_view file) {
279 auto file_p = GetFile(file);
280
281 if (file_p == nullptr) {
282 return false;
283 }
284
285 return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
286 }
287
288 bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
289 const std::string& file) {
290 auto file_p = GetFile(file);
291 if (file_p == nullptr)
292 return false;
293 return ReplaceFileWithSubdirectory(file_p, function(file_p));
294 }
295
296 // Returns the full path of this directory as a string, recursively 267 // Returns the full path of this directory as a string, recursively
297 virtual std::string GetFullPath() const; 268 virtual std::string GetFullPath() const;
298
299protected:
300 // Backend for InterpretAsDirectory.
301 // Removes all references to file and adds a reference to dir in the directory's implementation.
302 virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
303}; 269};
304 270
305// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work 271// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index bfee01725..338e398da 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -126,7 +126,4 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) {
126 return true; 126 return true;
127} 127}
128 128
129bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
130 return false;
131}
132} // namespace FileSys 129} // namespace FileSys
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
index d85310f57..8a25c3428 100644
--- a/src/core/file_sys/vfs_layered.h
+++ b/src/core/file_sys/vfs_layered.h
@@ -39,9 +39,6 @@ public:
39 bool DeleteFile(std::string_view name) override; 39 bool DeleteFile(std::string_view name) override;
40 bool Rename(std::string_view name) override; 40 bool Rename(std::string_view name) override;
41 41
42protected:
43 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
44
45private: 42private:
46 std::vector<VirtualDir> dirs; 43 std::vector<VirtualDir> dirs;
47 std::string name; 44 std::string name;
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index a4c6719a0..c96f88488 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -57,11 +57,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
57 return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); 57 return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
58} 58}
59 59
60boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { 60std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
61 if (r_offset < size) 61 if (r_offset < size)
62 return file->ReadByte(offset + r_offset); 62 return file->ReadByte(offset + r_offset);
63 63
64 return boost::none; 64 return {};
65} 65}
66 66
67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { 67std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 8062702a7..f7b7a3256 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -29,7 +29,7 @@ public:
29 bool IsReadable() const override; 29 bool IsReadable() const override;
30 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; 30 std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
31 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; 31 std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
32 boost::optional<u8> ReadByte(std::size_t offset) const override; 32 std::optional<u8> ReadByte(std::size_t offset) const override;
33 std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; 33 std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
34 std::vector<u8> ReadAllBytes() const override; 34 std::vector<u8> ReadAllBytes() const override;
35 bool WriteByte(u8 data, std::size_t offset) override; 35 bool WriteByte(u8 data, std::size_t offset) override;
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 9defad04c..e21300a7c 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -430,7 +430,4 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
430 return out; 430 return out;
431} 431}
432 432
433bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
434 return false;
435}
436} // namespace FileSys 433} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 5b61db90d..a0a857a31 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -100,9 +100,6 @@ public:
100 std::string GetFullPath() const override; 100 std::string GetFullPath() const override;
101 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; 101 std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
102 102
103protected:
104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
105
106private: 103private:
107 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); 104 RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
108 105
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 44fab51d1..9f5a90b1b 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -53,10 +53,10 @@ public:
53 return 0; 53 return 0;
54 } 54 }
55 55
56 boost::optional<u8> ReadByte(std::size_t offset) const override { 56 std::optional<u8> ReadByte(std::size_t offset) const override {
57 if (offset < size) 57 if (offset < size)
58 return value; 58 return value;
59 return boost::none; 59 return {};
60 } 60 }
61 61
62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { 62 std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h
new file mode 100644
index 000000000..6215ed7af
--- /dev/null
+++ b/src/core/file_sys/vfs_types.h
@@ -0,0 +1,21 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9namespace FileSys {
10
11class VfsDirectory;
12class VfsFile;
13class VfsFilesystem;
14
15// Declarations for Vfs* pointer types
16
17using VirtualDir = std::shared_ptr<VfsDirectory>;
18using VirtualFile = std::shared_ptr<VfsFile>;
19using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
20
21} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 389c7e003..808f31e81 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -132,11 +132,4 @@ void VectorVfsDirectory::AddFile(VirtualFile file) {
132void VectorVfsDirectory::AddDirectory(VirtualDir dir) { 132void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
133 dirs.push_back(std::move(dir)); 133 dirs.push_back(std::move(dir));
134} 134}
135
136bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
137 if (!DeleteFile(file->GetName()))
138 return false;
139 dirs.emplace_back(std::move(dir));
140 return true;
141}
142} // namespace FileSys 135} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index 48a414c98..3e3f790c3 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -57,9 +57,6 @@ public:
57 virtual void AddFile(VirtualFile file); 57 virtual void AddFile(VirtualFile file);
58 virtual void AddDirectory(VirtualDir dir); 58 virtual void AddDirectory(VirtualDir dir);
59 59
60protected:
61 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
62
63private: 60private:
64 std::vector<VirtualFile> files; 61 std::vector<VirtualFile> files;
65 std::vector<VirtualDir> dirs; 62 std::vector<VirtualDir> dirs;
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index b2b164368..eec51c64e 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -163,7 +163,4 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
163 return file->GetContainingDirectory(); 163 return file->GetContainingDirectory();
164} 164}
165 165
166bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
167 return false;
168}
169} // namespace FileSys 166} // namespace FileSys
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 8fedd8585..7704dee90 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -51,9 +51,6 @@ public:
51 51
52 std::shared_ptr<VfsDirectory> GetParentDirectory() const override; 52 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
53 53
54protected:
55 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
56
57private: 54private:
58 Loader::ResultStatus Parse(std::string_view path); 55 Loader::ResultStatus Parse(std::string_view path);
59 56
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index e961ef121..bdcc889e0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -207,7 +207,7 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
207 207
208static Kernel::Thread* FindThreadById(int id) { 208static Kernel::Thread* FindThreadById(int id) {
209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 209 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
210 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 210 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
211 for (auto& thread : threads) { 211 for (auto& thread : threads) {
212 if (thread->GetThreadID() == static_cast<u32>(id)) { 212 if (thread->GetThreadID() == static_cast<u32>(id)) {
213 current_core = core; 213 current_core = core;
@@ -597,7 +597,7 @@ static void HandleQuery() {
597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { 597 } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
598 std::string val = "m"; 598 std::string val = "m";
599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 599 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
600 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 600 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
601 for (const auto& thread : threads) { 601 for (const auto& thread : threads) {
602 val += fmt::format("{:x}", thread->GetThreadID()); 602 val += fmt::format("{:x}", thread->GetThreadID());
603 val += ","; 603 val += ",";
@@ -612,7 +612,7 @@ static void HandleQuery() {
612 buffer += "l<?xml version=\"1.0\"?>"; 612 buffer += "l<?xml version=\"1.0\"?>";
613 buffer += "<threads>"; 613 buffer += "<threads>";
614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { 614 for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
615 const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); 615 const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
616 for (const auto& thread : threads) { 616 for (const auto& thread : threads) {
617 buffer += 617 buffer +=
618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", 618 fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 419f45896..ed84197b3 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -14,11 +14,6 @@ namespace IPC {
14/// Size of the command buffer area, in 32-bit words. 14/// Size of the command buffer area, in 32-bit words.
15constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); 15constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
16 16
17// These errors are commonly returned by invalid IPC translations, so alias them here for
18// convenience.
19// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
20constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
21
22enum class ControlCommand : u32 { 17enum class ControlCommand : u32 {
23 ConvertSessionToDomain = 0, 18 ConvertSessionToDomain = 0,
24 ConvertDomainToSession = 1, 19 ConvertDomainToSession = 1,
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index a4bfe2eb0..0a7142ada 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -117,8 +117,7 @@ public:
117 117
118 AlignWithPadding(); 118 AlignWithPadding();
119 119
120 const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr}; 120 if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) {
121 if (context.Session()->IsDomain() && request_has_domain_header) {
122 IPC::DomainMessageHeader domain_header{}; 121 IPC::DomainMessageHeader domain_header{};
123 domain_header.num_objects = num_domain_objects; 122 domain_header.num_objects = num_domain_objects;
124 PushRaw(domain_header); 123 PushRaw(domain_header);
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index ebf193930..57157beb4 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -39,7 +39,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
39 std::vector<SharedPtr<Thread>>& waiting_threads, 39 std::vector<SharedPtr<Thread>>& waiting_threads,
40 VAddr arb_addr) { 40 VAddr arb_addr) {
41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
42 const auto& thread_list = scheduler->GetThreadList(); 42 const auto& thread_list = scheduler.GetThreadList();
43 43
44 for (const auto& thread : thread_list) { 44 for (const auto& thread : thread_list) {
45 if (thread->GetArbiterWaitAddress() == arb_addr) 45 if (thread->GetArbiterWaitAddress() == arb_addr)
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index e5fa67ae8..ee698c8a7 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -10,11 +10,6 @@ namespace Kernel {
10 10
11namespace ErrCodes { 11namespace ErrCodes {
12enum { 12enum {
13 // TODO(Subv): Remove these 3DS OS error codes.
14 SessionClosedByRemote = 26,
15 NoPendingSessions = 35,
16 InvalidBufferDescriptor = 48,
17
18 // Confirmed Switch OS error codes 13 // Confirmed Switch OS error codes
19 MaxConnectionsReached = 7, 14 MaxConnectionsReached = 7,
20 InvalidSize = 101, 15 InvalidSize = 101,
@@ -22,9 +17,11 @@ enum {
22 HandleTableFull = 105, 17 HandleTableFull = 105,
23 InvalidMemoryState = 106, 18 InvalidMemoryState = 106,
24 InvalidMemoryPermissions = 108, 19 InvalidMemoryPermissions = 108,
20 InvalidMemoryRange = 110,
25 InvalidThreadPriority = 112, 21 InvalidThreadPriority = 112,
26 InvalidProcessorId = 113, 22 InvalidProcessorId = 113,
27 InvalidHandle = 114, 23 InvalidHandle = 114,
24 InvalidPointer = 115,
28 InvalidCombination = 116, 25 InvalidCombination = 116,
29 Timeout = 117, 26 Timeout = 117,
30 SynchronizationCanceled = 118, 27 SynchronizationCanceled = 118,
@@ -32,6 +29,7 @@ enum {
32 InvalidEnumValue = 120, 29 InvalidEnumValue = 120,
33 NoSuchEntry = 121, 30 NoSuchEntry = 121,
34 AlreadyRegistered = 122, 31 AlreadyRegistered = 122,
32 SessionClosed = 123,
35 InvalidState = 125, 33 InvalidState = 125,
36 ResourceLimitExceeded = 132, 34 ResourceLimitExceeded = 132,
37}; 35};
@@ -40,22 +38,19 @@ enum {
40// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always 38// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
41// double check that the code matches before re-using the constant. 39// double check that the code matches before re-using the constant.
42 40
43// TODO(bunnei): Replace -1 with correct errors for Switch OS
44constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); 41constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
45constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); 42constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
46constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); 43constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
47constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, 44constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
48 ErrCodes::MaxConnectionsReached); 45 ErrCodes::MaxConnectionsReached);
49constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); 46constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
50constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
51constexpr ResultCode ERR_INVALID_COMBINATION(-1);
52constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, 47constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
53 ErrCodes::InvalidCombination); 48 ErrCodes::InvalidCombination);
54constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
55constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); 49constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
56constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); 50constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
57constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, 51constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
58 ErrCodes::InvalidMemoryPermissions); 52 ErrCodes::InvalidMemoryPermissions);
53constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
59constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 54constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
60constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 55constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
61constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); 56constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
@@ -63,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea
63constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 58constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
64constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, 59constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
65 ErrCodes::InvalidThreadPriority); 60 ErrCodes::InvalidThreadPriority);
66constexpr ResultCode ERR_INVALID_POINTER(-1); 61constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
67constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
68constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
69/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
70constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
71constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); 62constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
72constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); 63constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
73/// Returned when Accept() is called on a port with no sessions to be accepted.
74constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
75 64
76} // namespace Kernel 65} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index edad5f1b1..68d5376cb 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses
77 77
78HLERequestContext::~HLERequestContext() = default; 78HLERequestContext::~HLERequestContext() = default;
79 79
80void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { 80void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
81 bool incoming) {
81 IPC::RequestParser rp(src_cmdbuf); 82 IPC::RequestParser rp(src_cmdbuf);
82 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); 83 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
83 84
@@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
94 rp.Skip(2, false); 95 rp.Skip(2, false);
95 } 96 }
96 if (incoming) { 97 if (incoming) {
97 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
98
99 // Populate the object lists with the data in the IPC request. 98 // Populate the object lists with the data in the IPC request.
100 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { 99 for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
101 copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>())); 100 copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
@@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
189 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. 188 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
190} 189}
191 190
192ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, 191ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
193 Process& src_process, 192 u32_le* src_cmdbuf) {
194 HandleTable& src_table) { 193 ParseCommandBuffer(handle_table, src_cmdbuf, true);
195 ParseCommandBuffer(src_cmdbuf, true);
196 if (command_header->type == IPC::CommandType::Close) { 194 if (command_header->type == IPC::CommandType::Close) {
197 // Close does not populate the rest of the IPC header 195 // Close does not populate the rest of the IPC header
198 return RESULT_SUCCESS; 196 return RESULT_SUCCESS;
@@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
207 return RESULT_SUCCESS; 205 return RESULT_SUCCESS;
208} 206}
209 207
210ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { 208ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
209 auto& owner_process = *thread.GetOwnerProcess();
210 auto& handle_table = owner_process.GetHandleTable();
211
211 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; 212 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
212 Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), 213 Memory::ReadBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
213 dst_cmdbuf.size() * sizeof(u32)); 214 dst_cmdbuf.size() * sizeof(u32));
214 215
215 // The header was already built in the internal command buffer. Attempt to parse it to verify 216 // The header was already built in the internal command buffer. Attempt to parse it to verify
216 // the integrity and then copy it over to the target command buffer. 217 // the integrity and then copy it over to the target command buffer.
217 ParseCommandBuffer(cmd_buf.data(), false); 218 ParseCommandBuffer(handle_table, cmd_buf.data(), false);
218 219
219 // The data_size already includes the payload header, the padding and the domain header. 220 // The data_size already includes the payload header, the padding and the domain header.
220 std::size_t size = data_payload_offset + command_header->data_size - 221 std::size_t size = data_payload_offset + command_header->data_size -
@@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
236 ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy); 237 ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
237 ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move); 238 ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
238 239
239 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
240
241 // We don't make a distinction between copy and move handles when translating since HLE 240 // We don't make a distinction between copy and move handles when translating since HLE
242 // services don't deal with handles directly. However, the guest applications might check 241 // services don't deal with handles directly. However, the guest applications might check
243 // for specific values in each of these descriptors. 242 // for specific values in each of these descriptors.
@@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
268 } 267 }
269 268
270 // Copy the translated command buffer back into the thread's command buffer area. 269 // Copy the translated command buffer back into the thread's command buffer area.
271 Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), 270 Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
272 dst_cmdbuf.size() * sizeof(u32)); 271 dst_cmdbuf.size() * sizeof(u32));
273 272
274 return RESULT_SUCCESS; 273 return RESULT_SUCCESS;
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 894479ee0..a38e34b74 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -24,10 +24,10 @@ class ServiceFrameworkBase;
24namespace Kernel { 24namespace Kernel {
25 25
26class Domain; 26class Domain;
27class Event;
27class HandleTable; 28class HandleTable;
28class HLERequestContext; 29class HLERequestContext;
29class Process; 30class Process;
30class Event;
31 31
32/** 32/**
33 * Interface implemented by HLE Session handlers. 33 * Interface implemented by HLE Session handlers.
@@ -126,13 +126,12 @@ public:
126 u64 timeout, WakeupCallback&& callback, 126 u64 timeout, WakeupCallback&& callback,
127 Kernel::SharedPtr<Kernel::Event> event = nullptr); 127 Kernel::SharedPtr<Kernel::Event> event = nullptr);
128 128
129 void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
130
131 /// Populates this context with data from the requesting process/thread. 129 /// Populates this context with data from the requesting process/thread.
132 ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, 130 ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
133 HandleTable& src_table); 131 u32_le* src_cmdbuf);
132
134 /// Writes data from this context back to the requesting process/thread. 133 /// Writes data from this context back to the requesting process/thread.
135 ResultCode WriteToOutgoingCommandBuffer(const Thread& thread); 134 ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
136 135
137 u32_le GetCommand() const { 136 u32_le GetCommand() const {
138 return command; 137 return command;
@@ -162,8 +161,12 @@ public:
162 return buffer_c_desciptors; 161 return buffer_c_desciptors;
163 } 162 }
164 163
165 const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { 164 const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
166 return domain_message_header; 165 return domain_message_header.get();
166 }
167
168 bool HasDomainMessageHeader() const {
169 return domain_message_header != nullptr;
167 } 170 }
168 171
169 /// Helper function to read a buffer using the appropriate buffer descriptor 172 /// Helper function to read a buffer using the appropriate buffer descriptor
@@ -255,6 +258,8 @@ public:
255 std::string Description() const; 258 std::string Description() const;
256 259
257private: 260private:
261 void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
262
258 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 263 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
259 SharedPtr<Kernel::ServerSession> server_session; 264 SharedPtr<Kernel::ServerSession> server_session;
260 // TODO(yuriks): Check common usage of this and optimize size accordingly 265 // TODO(yuriks): Check common usage of this and optimize size accordingly
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 98eb74298..1fd4ba5d2 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -32,7 +32,7 @@ namespace Kernel {
32 */ 32 */
33static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) { 33static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
34 const auto proper_handle = static_cast<Handle>(thread_handle); 34 const auto proper_handle = static_cast<Handle>(thread_handle);
35 auto& system = Core::System::GetInstance(); 35 const auto& system = Core::System::GetInstance();
36 36
37 // Lock the global kernel mutex when we enter the kernel HLE. 37 // Lock the global kernel mutex when we enter the kernel HLE.
38 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); 38 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
@@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
90/// The timer callback event, called when a timer is fired 90/// The timer callback event, called when a timer is fired
91static void TimerCallback(u64 timer_handle, int cycles_late) { 91static void TimerCallback(u64 timer_handle, int cycles_late) {
92 const auto proper_handle = static_cast<Handle>(timer_handle); 92 const auto proper_handle = static_cast<Handle>(timer_handle);
93 auto& system = Core::System::GetInstance(); 93 const auto& system = Core::System::GetInstance();
94 SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle); 94 SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
95 95
96 if (timer == nullptr) { 96 if (timer == nullptr) {
@@ -116,9 +116,8 @@ struct KernelCore::Impl {
116 next_thread_id = 1; 116 next_thread_id = 1;
117 117
118 process_list.clear(); 118 process_list.clear();
119 current_process.reset(); 119 current_process = nullptr;
120 120
121 handle_table.Clear();
122 resource_limits.fill(nullptr); 121 resource_limits.fill(nullptr);
123 122
124 thread_wakeup_callback_handle_table.Clear(); 123 thread_wakeup_callback_handle_table.Clear();
@@ -207,9 +206,8 @@ struct KernelCore::Impl {
207 206
208 // Lists all processes that exist in the current session. 207 // Lists all processes that exist in the current session.
209 std::vector<SharedPtr<Process>> process_list; 208 std::vector<SharedPtr<Process>> process_list;
210 SharedPtr<Process> current_process; 209 Process* current_process = nullptr;
211 210
212 Kernel::HandleTable handle_table;
213 std::array<SharedPtr<ResourceLimit>, 4> resource_limits; 211 std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
214 212
215 /// The event type of the generic timer callback event 213 /// The event type of the generic timer callback event
@@ -241,14 +239,6 @@ void KernelCore::Shutdown() {
241 impl->Shutdown(); 239 impl->Shutdown();
242} 240}
243 241
244Kernel::HandleTable& KernelCore::HandleTable() {
245 return impl->handle_table;
246}
247
248const Kernel::HandleTable& KernelCore::HandleTable() const {
249 return impl->handle_table;
250}
251
252SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory( 242SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
253 ResourceLimitCategory category) const { 243 ResourceLimitCategory category) const {
254 return impl->resource_limits.at(static_cast<std::size_t>(category)); 244 return impl->resource_limits.at(static_cast<std::size_t>(category));
@@ -266,15 +256,15 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
266 impl->process_list.push_back(std::move(process)); 256 impl->process_list.push_back(std::move(process));
267} 257}
268 258
269void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) { 259void KernelCore::MakeCurrentProcess(Process* process) {
270 impl->current_process = std::move(process); 260 impl->current_process = process;
271} 261}
272 262
273SharedPtr<Process>& KernelCore::CurrentProcess() { 263Process* KernelCore::CurrentProcess() {
274 return impl->current_process; 264 return impl->current_process;
275} 265}
276 266
277const SharedPtr<Process>& KernelCore::CurrentProcess() const { 267const Process* KernelCore::CurrentProcess() const {
278 return impl->current_process; 268 return impl->current_process;
279} 269}
280 270
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c0771ecf0..7f822d524 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -47,12 +47,6 @@ public:
47 /// Clears all resources in use by the kernel instance. 47 /// Clears all resources in use by the kernel instance.
48 void Shutdown(); 48 void Shutdown();
49 49
50 /// Provides a reference to the handle table.
51 Kernel::HandleTable& HandleTable();
52
53 /// Provides a const reference to the handle table.
54 const Kernel::HandleTable& HandleTable() const;
55
56 /// Retrieves a shared pointer to a ResourceLimit identified by the given category. 50 /// Retrieves a shared pointer to a ResourceLimit identified by the given category.
57 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const; 51 SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
58 52
@@ -66,13 +60,13 @@ public:
66 void AppendNewProcess(SharedPtr<Process> process); 60 void AppendNewProcess(SharedPtr<Process> process);
67 61
68 /// Makes the given process the new current process. 62 /// Makes the given process the new current process.
69 void MakeCurrentProcess(SharedPtr<Process> process); 63 void MakeCurrentProcess(Process* process);
70 64
71 /// Retrieves a reference to the current process. 65 /// Retrieves a pointer to the current process.
72 SharedPtr<Process>& CurrentProcess(); 66 Process* CurrentProcess();
73 67
74 /// Retrieves a const reference to the current process. 68 /// Retrieves a const pointer to the current process.
75 const SharedPtr<Process>& CurrentProcess() const; 69 const Process* CurrentProcess() const;
76 70
77 /// Adds a port to the named port table 71 /// Adds a port to the named port table
78 void AddNamedPort(std::string name, SharedPtr<ClientPort> port); 72 void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index dd541ffcc..0743670ad 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -6,8 +6,6 @@
6#include <utility> 6#include <utility>
7#include <vector> 7#include <vector>
8 8
9#include <boost/range/algorithm_ext/erase.hpp>
10
11#include "common/assert.h" 9#include "common/assert.h"
12#include "core/core.h" 10#include "core/core.h"
13#include "core/hle/kernel/errors.h" 11#include "core/hle/kernel/errors.h"
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index d51562d92..d87a62bb9 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
25 case HandleType::Process: 25 case HandleType::Process:
26 case HandleType::AddressArbiter: 26 case HandleType::AddressArbiter:
27 case HandleType::ResourceLimit: 27 case HandleType::ResourceLimit:
28 case HandleType::CodeSet:
29 case HandleType::ClientPort: 28 case HandleType::ClientPort:
30 case HandleType::ClientSession: 29 case HandleType::ClientSession:
31 return false; 30 return false;
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index 9eb72315c..c9f4d0bb3 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -26,7 +26,6 @@ enum class HandleType : u32 {
26 AddressArbiter, 26 AddressArbiter,
27 Timer, 27 Timer,
28 ResourceLimit, 28 ResourceLimit,
29 CodeSet,
30 ClientPort, 29 ClientPort,
31 ServerPort, 30 ServerPort,
32 ClientSession, 31 ClientSession,
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index fb0027a71..420218d59 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,13 +20,7 @@
20 20
21namespace Kernel { 21namespace Kernel {
22 22
23SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) { 23CodeSet::CodeSet() = default;
24 SharedPtr<CodeSet> codeset(new CodeSet(kernel));
25 codeset->name = std::move(name);
26 return codeset;
27}
28
29CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
30CodeSet::~CodeSet() = default; 24CodeSet::~CodeSet() = default;
31 25
32SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { 26SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -159,11 +153,11 @@ void Process::PrepareForTermination() {
159 } 153 }
160 }; 154 };
161 155
162 auto& system = Core::System::GetInstance(); 156 const auto& system = Core::System::GetInstance();
163 stop_threads(system.Scheduler(0)->GetThreadList()); 157 stop_threads(system.Scheduler(0).GetThreadList());
164 stop_threads(system.Scheduler(1)->GetThreadList()); 158 stop_threads(system.Scheduler(1).GetThreadList());
165 stop_threads(system.Scheduler(2)->GetThreadList()); 159 stop_threads(system.Scheduler(2).GetThreadList());
166 stop_threads(system.Scheduler(3)->GetThreadList()); 160 stop_threads(system.Scheduler(3).GetThreadList());
167} 161}
168 162
169/** 163/**
@@ -224,20 +218,26 @@ void Process::FreeTLSSlot(VAddr tls_address) {
224 tls_slots[tls_page].reset(tls_slot); 218 tls_slots[tls_page].reset(tls_slot);
225} 219}
226 220
227void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 221void Process::LoadModule(CodeSet module_, VAddr base_addr) {
228 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, 222 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
229 MemoryState memory_state) { 223 MemoryState memory_state) {
230 auto vma = vm_manager 224 const auto vma = vm_manager
231 .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, 225 .MapMemoryBlock(segment.addr + base_addr, module_.memory,
232 segment.size, memory_state) 226 segment.offset, segment.size, memory_state)
233 .Unwrap(); 227 .Unwrap();
234 vm_manager.Reprotect(vma, permissions); 228 vm_manager.Reprotect(vma, permissions);
235 }; 229 };
236 230
237 // Map CodeSet segments 231 // Map CodeSet segments
238 MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); 232 MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
239 MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); 233 MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
240 MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 234 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
235
236 // Clear instruction cache in CPU JIT
237 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
238 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
239 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
240 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
241} 241}
242 242
243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 243ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 590e0c73d..8d2616c79 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -13,6 +13,7 @@
13#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
14#include "common/bit_field.h" 14#include "common/bit_field.h"
15#include "common/common_types.h" 15#include "common/common_types.h"
16#include "core/hle/kernel/handle_table.h"
16#include "core/hle/kernel/object.h" 17#include "core/hle/kernel/object.h"
17#include "core/hle/kernel/thread.h" 18#include "core/hle/kernel/thread.h"
18#include "core/hle/kernel/vm_manager.h" 19#include "core/hle/kernel/vm_manager.h"
@@ -24,6 +25,7 @@ class ProgramMetadata;
24namespace Kernel { 25namespace Kernel {
25 26
26class KernelCore; 27class KernelCore;
28class ResourceLimit;
27 29
28struct AddressMapping { 30struct AddressMapping {
29 // Address and size must be page-aligned 31 // Address and size must be page-aligned
@@ -57,30 +59,33 @@ union ProcessFlags {
57 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). 59 BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
58}; 60};
59 61
60enum class ProcessStatus { Created, Running, Exited }; 62/**
61 63 * Indicates the status of a Process instance.
62class ResourceLimit; 64 *
65 * @note These match the values as used by kernel,
66 * so new entries should only be added if RE
67 * shows that a new value has been introduced.
68 */
69enum class ProcessStatus {
70 Created,
71 CreatedWithDebuggerAttached,
72 Running,
73 WaitingForDebuggerToAttach,
74 DebuggerAttached,
75 Exiting,
76 Exited,
77 DebugBreak,
78};
63 79
64struct CodeSet final : public Object { 80struct CodeSet final {
65 struct Segment { 81 struct Segment {
66 std::size_t offset = 0; 82 std::size_t offset = 0;
67 VAddr addr = 0; 83 VAddr addr = 0;
68 u32 size = 0; 84 u32 size = 0;
69 }; 85 };
70 86
71 static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name); 87 explicit CodeSet();
72 88 ~CodeSet();
73 std::string GetTypeName() const override {
74 return "CodeSet";
75 }
76 std::string GetName() const override {
77 return name;
78 }
79
80 static const HandleType HANDLE_TYPE = HandleType::CodeSet;
81 HandleType GetHandleType() const override {
82 return HANDLE_TYPE;
83 }
84 89
85 Segment& CodeSegment() { 90 Segment& CodeSegment() {
86 return segments[0]; 91 return segments[0];
@@ -109,14 +114,7 @@ struct CodeSet final : public Object {
109 std::shared_ptr<std::vector<u8>> memory; 114 std::shared_ptr<std::vector<u8>> memory;
110 115
111 std::array<Segment, 3> segments; 116 std::array<Segment, 3> segments;
112 VAddr entrypoint; 117 VAddr entrypoint = 0;
113
114 /// Name of the process
115 std::string name;
116
117private:
118 explicit CodeSet(KernelCore& kernel);
119 ~CodeSet() override;
120}; 118};
121 119
122class Process final : public Object { 120class Process final : public Object {
@@ -145,6 +143,16 @@ public:
145 return vm_manager; 143 return vm_manager;
146 } 144 }
147 145
146 /// Gets a reference to the process' handle table.
147 HandleTable& GetHandleTable() {
148 return handle_table;
149 }
150
151 /// Gets a const reference to the process' handle table.
152 const HandleTable& GetHandleTable() const {
153 return handle_table;
154 }
155
148 /// Gets the current status of the process 156 /// Gets the current status of the process
149 ProcessStatus GetStatus() const { 157 ProcessStatus GetStatus() const {
150 return status; 158 return status;
@@ -194,6 +202,16 @@ public:
194 return is_64bit_process; 202 return is_64bit_process;
195 } 203 }
196 204
205 /// Gets the total running time of the process instance in ticks.
206 u64 GetCPUTimeTicks() const {
207 return total_process_running_time_ticks;
208 }
209
210 /// Updates the total running time, adding the given ticks to it.
211 void UpdateCPUTimeTicks(u64 ticks) {
212 total_process_running_time_ticks += ticks;
213 }
214
197 /** 215 /**
198 * Loads process-specifics configuration info with metadata provided 216 * Loads process-specifics configuration info with metadata provided
199 * by an executable. 217 * by an executable.
@@ -219,7 +237,7 @@ public:
219 */ 237 */
220 void PrepareForTermination(); 238 void PrepareForTermination();
221 239
222 void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); 240 void LoadModule(CodeSet module_, VAddr base_addr);
223 241
224 /////////////////////////////////////////////////////////////////////////////////////////////// 242 ///////////////////////////////////////////////////////////////////////////////////////////////
225 // Memory Management 243 // Memory Management
@@ -297,6 +315,12 @@ private:
297 /// specified by metadata provided to the process during loading. 315 /// specified by metadata provided to the process during loading.
298 bool is_64bit_process = true; 316 bool is_64bit_process = true;
299 317
318 /// Total running time for the process in ticks.
319 u64 total_process_running_time_ticks = 0;
320
321 /// Per-process handle table for storing created object handles in.
322 HandleTable handle_table;
323
300 std::string name; 324 std::string name;
301}; 325};
302 326
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index cfd6e1bad..5a5f4cef1 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -10,6 +10,7 @@
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/core_timing.h" 12#include "core/core_timing.h"
13#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/process.h" 14#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/scheduler.h" 15#include "core/hle/kernel/scheduler.h"
15 16
@@ -34,6 +35,10 @@ Thread* Scheduler::GetCurrentThread() const {
34 return current_thread.get(); 35 return current_thread.get();
35} 36}
36 37
38u64 Scheduler::GetLastContextSwitchTicks() const {
39 return last_context_switch_time;
40}
41
37Thread* Scheduler::PopNextReadyThread() { 42Thread* Scheduler::PopNextReadyThread() {
38 Thread* next = nullptr; 43 Thread* next = nullptr;
39 Thread* thread = GetCurrentThread(); 44 Thread* thread = GetCurrentThread();
@@ -54,7 +59,10 @@ Thread* Scheduler::PopNextReadyThread() {
54} 59}
55 60
56void Scheduler::SwitchContext(Thread* new_thread) { 61void Scheduler::SwitchContext(Thread* new_thread) {
57 Thread* previous_thread = GetCurrentThread(); 62 Thread* const previous_thread = GetCurrentThread();
63 Process* const previous_process = Core::CurrentProcess();
64
65 UpdateLastContextSwitchTime(previous_thread, previous_process);
58 66
59 // Save context for previous thread 67 // Save context for previous thread
60 if (previous_thread) { 68 if (previous_thread) {
@@ -78,16 +86,14 @@ void Scheduler::SwitchContext(Thread* new_thread) {
78 // Cancel any outstanding wakeup events for this thread 86 // Cancel any outstanding wakeup events for this thread
79 new_thread->CancelWakeupTimer(); 87 new_thread->CancelWakeupTimer();
80 88
81 auto previous_process = Core::CurrentProcess();
82
83 current_thread = new_thread; 89 current_thread = new_thread;
84 90
85 ready_queue.remove(new_thread->GetPriority(), new_thread); 91 ready_queue.remove(new_thread->GetPriority(), new_thread);
86 new_thread->SetStatus(ThreadStatus::Running); 92 new_thread->SetStatus(ThreadStatus::Running);
87 93
88 const auto thread_owner_process = current_thread->GetOwnerProcess(); 94 auto* const thread_owner_process = current_thread->GetOwnerProcess();
89 if (previous_process != thread_owner_process) { 95 if (previous_process != thread_owner_process) {
90 Core::CurrentProcess() = thread_owner_process; 96 Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
91 SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); 97 SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
92 } 98 }
93 99
@@ -102,6 +108,22 @@ void Scheduler::SwitchContext(Thread* new_thread) {
102 } 108 }
103} 109}
104 110
111void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
112 const u64 prev_switch_ticks = last_context_switch_time;
113 const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
114 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
115
116 if (thread != nullptr) {
117 thread->UpdateCPUTimeTicks(update_ticks);
118 }
119
120 if (process != nullptr) {
121 process->UpdateCPUTimeTicks(update_ticks);
122 }
123
124 last_context_switch_time = most_recent_switch_ticks;
125}
126
105void Scheduler::Reschedule() { 127void Scheduler::Reschedule() {
106 std::lock_guard<std::mutex> lock(scheduler_mutex); 128 std::lock_guard<std::mutex> lock(scheduler_mutex);
107 129
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 2c94641ec..c63032b7d 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -17,6 +17,8 @@ class ARM_Interface;
17 17
18namespace Kernel { 18namespace Kernel {
19 19
20class Process;
21
20class Scheduler final { 22class Scheduler final {
21public: 23public:
22 explicit Scheduler(Core::ARM_Interface& cpu_core); 24 explicit Scheduler(Core::ARM_Interface& cpu_core);
@@ -31,6 +33,9 @@ public:
31 /// Gets the current running thread 33 /// Gets the current running thread
32 Thread* GetCurrentThread() const; 34 Thread* GetCurrentThread() const;
33 35
36 /// Gets the timestamp for the last context switch in ticks.
37 u64 GetLastContextSwitchTicks() const;
38
34 /// Adds a new thread to the scheduler 39 /// Adds a new thread to the scheduler
35 void AddThread(SharedPtr<Thread> thread, u32 priority); 40 void AddThread(SharedPtr<Thread> thread, u32 priority);
36 41
@@ -64,6 +69,19 @@ private:
64 */ 69 */
65 void SwitchContext(Thread* new_thread); 70 void SwitchContext(Thread* new_thread);
66 71
72 /**
73 * Called on every context switch to update the internal timestamp
74 * This also updates the running time ticks for the given thread and
75 * process using the following difference:
76 *
77 * ticks += most_recent_ticks - last_context_switch_ticks
78 *
79 * The internal tick timestamp for the scheduler is simply the
80 * most recent tick count retrieved. No special arithmetic is
81 * applied to it.
82 */
83 void UpdateLastContextSwitchTime(Thread* thread, Process* process);
84
67 /// Lists all thread ids that aren't deleted/etc. 85 /// Lists all thread ids that aren't deleted/etc.
68 std::vector<SharedPtr<Thread>> thread_list; 86 std::vector<SharedPtr<Thread>> thread_list;
69 87
@@ -73,6 +91,7 @@ private:
73 SharedPtr<Thread> current_thread = nullptr; 91 SharedPtr<Thread> current_thread = nullptr;
74 92
75 Core::ARM_Interface& cpu_core; 93 Core::ARM_Interface& cpu_core;
94 u64 last_context_switch_time = 0;
76 95
77 static std::mutex scheduler_mutex; 96 static std::mutex scheduler_mutex;
78}; 97};
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 3792e3e18..d6ceeb2da 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
18 18
19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { 19ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
20 if (pending_sessions.empty()) { 20 if (pending_sessions.empty()) {
21 return ERR_NO_PENDING_SESSIONS; 21 return ERR_NOT_FOUND;
22 } 22 }
23 23
24 auto session = std::move(pending_sessions.back()); 24 auto session = std::move(pending_sessions.back());
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
28 28
29bool ServerPort::ShouldWait(Thread* thread) const { 29bool ServerPort::ShouldWait(Thread* thread) const {
30 // If there are no pending sessions, we wait until a new one is added. 30 // If there are no pending sessions, we wait until a new one is added.
31 return pending_sessions.size() == 0; 31 return pending_sessions.empty();
32} 32}
33 33
34void ServerPort::Acquire(Thread* thread) { 34void ServerPort::Acquire(Thread* thread) {
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 1ece691c7..80897f3a4 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) {
63} 63}
64 64
65ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { 65ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
66 auto& domain_message_header = context.GetDomainMessageHeader(); 66 auto* const domain_message_header = context.GetDomainMessageHeader();
67 if (domain_message_header) { 67 if (domain_message_header) {
68 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs 68 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
69 context.SetDomainRequestHandlers(domain_request_handlers); 69 context.SetDomainRequestHandlers(domain_request_handlers);
@@ -107,12 +107,11 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
107 // similar. 107 // similar.
108 Kernel::HLERequestContext context(this); 108 Kernel::HLERequestContext context(this);
109 u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); 109 u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
110 context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), 110 context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
111 kernel.HandleTable());
112 111
113 ResultCode result = RESULT_SUCCESS; 112 ResultCode result = RESULT_SUCCESS;
114 // If the session has been converted to a domain, handle the domain request 113 // If the session has been converted to a domain, handle the domain request
115 if (IsDomain() && context.GetDomainMessageHeader()) { 114 if (IsDomain() && context.HasDomainMessageHeader()) {
116 result = HandleDomainSyncRequest(context); 115 result = HandleDomainSyncRequest(context);
117 // If there is no domain header, the regular session handler is used 116 // If there is no domain header, the regular session handler is used
118 } else if (hle_handler != nullptr) { 117 } else if (hle_handler != nullptr) {
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index d061e6155..a016a86b6 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
80 80
81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, 81ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
82 MemoryPermission other_permissions) { 82 MemoryPermission other_permissions) {
83 83 const MemoryPermission own_other_permissions =
84 MemoryPermission own_other_permissions =
85 target_process == owner_process ? this->permissions : this->other_permissions; 84 target_process == owner_process ? this->permissions : this->other_permissions;
86 85
87 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare 86 // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
88 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { 87 if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
89 return ERR_INVALID_COMBINATION; 88 return ERR_INVALID_MEMORY_PERMISSIONS;
90 } 89 }
91 90
92 // Error out if the requested permissions don't match what the creator process allows. 91 // Error out if the requested permissions don't match what the creator process allows.
93 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { 92 if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
94 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", 93 LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
95 GetObjectId(), address, name); 94 GetObjectId(), address, name);
96 return ERR_INVALID_COMBINATION; 95 return ERR_INVALID_MEMORY_PERMISSIONS;
97 } 96 }
98 97
99 // Error out if the provided permissions are not compatible with what the creator process needs. 98 // Error out if the provided permissions are not compatible with what the creator process needs.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 6c4af7e47..c7c579aaf 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -8,6 +8,7 @@
8#include <mutex> 8#include <mutex>
9#include <vector> 9#include <vector>
10 10
11#include "common/alignment.h"
11#include "common/assert.h" 12#include "common/assert.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/microprofile.h" 14#include "common/microprofile.h"
@@ -36,8 +37,72 @@
36 37
37namespace Kernel { 38namespace Kernel {
38namespace { 39namespace {
39constexpr bool Is4KBAligned(VAddr address) { 40
40 return (address & 0xFFF) == 0; 41// Checks if address + size is greater than the given address
42// This can return false if the size causes an overflow of a 64-bit type
43// or if the given size is zero.
44constexpr bool IsValidAddressRange(VAddr address, u64 size) {
45 return address + size > address;
46}
47
48// Checks if a given address range lies within a larger address range.
49constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
50 VAddr address_range_end) {
51 const VAddr end_address = address + size - 1;
52 return address_range_begin <= address && end_address <= address_range_end - 1;
53}
54
55bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
56 return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
57 vm.GetAddressSpaceEndAddress());
58}
59
60bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
61 return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
62 vm.GetNewMapRegionEndAddress());
63}
64
65// Helper function that performs the common sanity checks for svcMapMemory
66// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
67// in the same order.
68ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
69 u64 size) {
70 if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
71 return ERR_INVALID_ADDRESS;
72 }
73
74 if (size == 0 || !Common::Is4KBAligned(size)) {
75 return ERR_INVALID_SIZE;
76 }
77
78 if (!IsValidAddressRange(dst_addr, size)) {
79 return ERR_INVALID_ADDRESS_STATE;
80 }
81
82 if (!IsValidAddressRange(src_addr, size)) {
83 return ERR_INVALID_ADDRESS_STATE;
84 }
85
86 if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
87 return ERR_INVALID_ADDRESS_STATE;
88 }
89
90 if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
91 return ERR_INVALID_MEMORY_RANGE;
92 }
93
94 const VAddr dst_end_address = dst_addr + size;
95 if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
96 vm_manager.GetHeapRegionEndAddress() > dst_addr) {
97 return ERR_INVALID_MEMORY_RANGE;
98 }
99
100 if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
101 vm_manager.GetMapRegionEndAddress() > dst_addr) {
102 return ERR_INVALID_MEMORY_RANGE;
103 }
104
105 return RESULT_SUCCESS;
41} 106}
42} // Anonymous namespace 107} // Anonymous namespace
43 108
@@ -69,15 +134,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
69 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 134 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
70 src_addr, size); 135 src_addr, size);
71 136
72 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 137 auto* const current_process = Core::CurrentProcess();
73 return ERR_INVALID_ADDRESS; 138 const auto& vm_manager = current_process->VMManager();
74 }
75 139
76 if (size == 0 || !Is4KBAligned(size)) { 140 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
77 return ERR_INVALID_SIZE; 141 if (result != RESULT_SUCCESS) {
142 return result;
78 } 143 }
79 144
80 return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); 145 return current_process->MirrorMemory(dst_addr, src_addr, size);
81} 146}
82 147
83/// Unmaps a region that was previously mapped with svcMapMemory 148/// Unmaps a region that was previously mapped with svcMapMemory
@@ -85,15 +150,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
85 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 150 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
86 src_addr, size); 151 src_addr, size);
87 152
88 if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { 153 auto* const current_process = Core::CurrentProcess();
89 return ERR_INVALID_ADDRESS; 154 const auto& vm_manager = current_process->VMManager();
90 }
91 155
92 if (size == 0 || !Is4KBAligned(size)) { 156 const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
93 return ERR_INVALID_SIZE; 157 if (result != RESULT_SUCCESS) {
158 return result;
94 } 159 }
95 160
96 return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); 161 return current_process->UnmapMemory(dst_addr, src_addr, size);
97} 162}
98 163
99/// Connect to an OS service given the port name, returns the handle to the port to out 164/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -124,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
124 CASCADE_RESULT(client_session, client_port->Connect()); 189 CASCADE_RESULT(client_session, client_port->Connect());
125 190
126 // Return the client session 191 // Return the client session
127 CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session)); 192 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
193 CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
128 return RESULT_SUCCESS; 194 return RESULT_SUCCESS;
129} 195}
130 196
131/// Makes a blocking IPC call to an OS service. 197/// Makes a blocking IPC call to an OS service.
132static ResultCode SendSyncRequest(Handle handle) { 198static ResultCode SendSyncRequest(Handle handle) {
133 auto& kernel = Core::System::GetInstance().Kernel(); 199 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
134 SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle); 200 SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
135 if (!session) { 201 if (!session) {
136 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); 202 LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
137 return ERR_INVALID_HANDLE; 203 return ERR_INVALID_HANDLE;
@@ -150,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) {
150static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { 216static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
151 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 217 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
152 218
153 auto& kernel = Core::System::GetInstance().Kernel(); 219 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
154 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 220 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
155 if (!thread) { 221 if (!thread) {
156 return ERR_INVALID_HANDLE; 222 return ERR_INVALID_HANDLE;
157 } 223 }
@@ -164,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
164static ResultCode GetProcessId(u32* process_id, Handle process_handle) { 230static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
165 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle); 231 LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
166 232
167 auto& kernel = Core::System::GetInstance().Kernel(); 233 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
168 const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle); 234 const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
169 if (!process) { 235 if (!process) {
170 return ERR_INVALID_HANDLE; 236 return ERR_INVALID_HANDLE;
171 } 237 }
@@ -208,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
208 274
209 using ObjectPtr = Thread::ThreadWaitObjects::value_type; 275 using ObjectPtr = Thread::ThreadWaitObjects::value_type;
210 Thread::ThreadWaitObjects objects(handle_count); 276 Thread::ThreadWaitObjects objects(handle_count);
211 auto& kernel = Core::System::GetInstance().Kernel(); 277 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
212 278
213 for (u64 i = 0; i < handle_count; ++i) { 279 for (u64 i = 0; i < handle_count; ++i) {
214 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); 280 const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
215 const auto object = kernel.HandleTable().Get<WaitObject>(handle); 281 const auto object = handle_table.Get<WaitObject>(handle);
216 282
217 if (object == nullptr) { 283 if (object == nullptr) {
218 return ERR_INVALID_HANDLE; 284 return ERR_INVALID_HANDLE;
@@ -260,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
260static ResultCode CancelSynchronization(Handle thread_handle) { 326static ResultCode CancelSynchronization(Handle thread_handle) {
261 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); 327 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
262 328
263 auto& kernel = Core::System::GetInstance().Kernel(); 329 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
264 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 330 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
265 if (!thread) { 331 if (!thread) {
266 return ERR_INVALID_HANDLE; 332 return ERR_INVALID_HANDLE;
267 } 333 }
@@ -285,7 +351,11 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
285 return ERR_INVALID_ADDRESS_STATE; 351 return ERR_INVALID_ADDRESS_STATE;
286 } 352 }
287 353
288 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 354 if (!Common::IsWordAligned(mutex_addr)) {
355 return ERR_INVALID_ADDRESS;
356 }
357
358 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
289 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, 359 return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
290 requesting_thread_handle); 360 requesting_thread_handle);
291} 361}
@@ -298,16 +368,87 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
298 return ERR_INVALID_ADDRESS_STATE; 368 return ERR_INVALID_ADDRESS_STATE;
299 } 369 }
300 370
371 if (!Common::IsWordAligned(mutex_addr)) {
372 return ERR_INVALID_ADDRESS;
373 }
374
301 return Mutex::Release(mutex_addr); 375 return Mutex::Release(mutex_addr);
302} 376}
303 377
378enum class BreakType : u32 {
379 Panic = 0,
380 AssertionFailed = 1,
381 PreNROLoad = 3,
382 PostNROLoad = 4,
383 PreNROUnload = 5,
384 PostNROUnload = 6,
385};
386
387struct BreakReason {
388 union {
389 u32 raw;
390 BitField<0, 30, BreakType> break_type;
391 BitField<31, 1, u32> signal_debugger;
392 };
393};
394
304/// Break program execution 395/// Break program execution
305static void Break(u64 reason, u64 info1, u64 info2) { 396static void Break(u32 reason, u64 info1, u64 info2) {
306 LOG_CRITICAL( 397 BreakReason break_reason{reason};
307 Debug_Emulated, 398
308 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", 399 switch (break_reason.break_type) {
309 reason, info1, info2); 400 case BreakType::Panic:
310 ASSERT(false); 401 LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
402 info1, info2);
403 break;
404 case BreakType::AssertionFailed:
405 LOG_CRITICAL(Debug_Emulated,
406 "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
407 info1, info2);
408 break;
409 case BreakType::PreNROLoad:
410 LOG_WARNING(
411 Debug_Emulated,
412 "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
413 info1, info2);
414 break;
415 case BreakType::PostNROLoad:
416 LOG_WARNING(Debug_Emulated,
417 "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
418 info2);
419 break;
420 case BreakType::PreNROUnload:
421 LOG_WARNING(
422 Debug_Emulated,
423 "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
424 info1, info2);
425 break;
426 case BreakType::PostNROUnload:
427 LOG_WARNING(Debug_Emulated,
428 "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
429 info2);
430 break;
431 default:
432 LOG_WARNING(
433 Debug_Emulated,
434 "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
435 static_cast<u32>(break_reason.break_type.Value()), info1, info2);
436 break;
437 }
438
439 if (!break_reason.signal_debugger) {
440 LOG_CRITICAL(
441 Debug_Emulated,
442 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
443 reason, info1, info2);
444 ASSERT(false);
445
446 Core::CurrentProcess()->PrepareForTermination();
447
448 // Kill the current thread
449 GetCurrentThread()->Stop();
450 Core::System::GetInstance().PrepareReschedule();
451 }
311} 452}
312 453
313/// Used to output a message on a debug hardware unit - does nothing on a retail unit 454/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -326,7 +467,38 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
326 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 467 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
327 info_sub_id, handle); 468 info_sub_id, handle);
328 469
329 const auto& current_process = Core::CurrentProcess(); 470 enum class GetInfoType : u64 {
471 // 1.0.0+
472 AllowedCpuIdBitmask = 0,
473 AllowedThreadPrioBitmask = 1,
474 MapRegionBaseAddr = 2,
475 MapRegionSize = 3,
476 HeapRegionBaseAddr = 4,
477 HeapRegionSize = 5,
478 TotalMemoryUsage = 6,
479 TotalHeapUsage = 7,
480 IsCurrentProcessBeingDebugged = 8,
481 ResourceHandleLimit = 9,
482 IdleTickCount = 10,
483 RandomEntropy = 11,
484 PerformanceCounter = 0xF0000002,
485 // 2.0.0+
486 ASLRRegionBaseAddr = 12,
487 ASLRRegionSize = 13,
488 NewMapRegionBaseAddr = 14,
489 NewMapRegionSize = 15,
490 // 3.0.0+
491 IsVirtualAddressMemoryEnabled = 16,
492 PersonalMmHeapUsage = 17,
493 TitleId = 18,
494 // 4.0.0+
495 PrivilegedProcessId = 19,
496 // 5.0.0+
497 UserExceptionContextAddr = 20,
498 ThreadTickCount = 0xF0000002,
499 };
500
501 const auto* current_process = Core::CurrentProcess();
330 const auto& vm_manager = current_process->VMManager(); 502 const auto& vm_manager = current_process->VMManager();
331 503
332 switch (static_cast<GetInfoType>(info_id)) { 504 switch (static_cast<GetInfoType>(info_id)) {
@@ -360,25 +532,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
360 case GetInfoType::RandomEntropy: 532 case GetInfoType::RandomEntropy:
361 *result = 0; 533 *result = 0;
362 break; 534 break;
363 case GetInfoType::AddressSpaceBaseAddr: 535 case GetInfoType::ASLRRegionBaseAddr:
364 *result = vm_manager.GetCodeRegionBaseAddress(); 536 *result = vm_manager.GetASLRRegionBaseAddress();
365 break; 537 break;
366 case GetInfoType::AddressSpaceSize: { 538 case GetInfoType::ASLRRegionSize:
367 const u64 width = vm_manager.GetAddressSpaceWidth(); 539 *result = vm_manager.GetASLRRegionSize();
368
369 switch (width) {
370 case 32:
371 *result = 0xFFE00000;
372 break;
373 case 36:
374 *result = 0xFF8000000;
375 break;
376 case 39:
377 *result = 0x7FF8000000;
378 break;
379 }
380 break; 540 break;
381 }
382 case GetInfoType::NewMapRegionBaseAddr: 541 case GetInfoType::NewMapRegionBaseAddr:
383 *result = vm_manager.GetNewMapRegionBaseAddress(); 542 *result = vm_manager.GetNewMapRegionBaseAddress();
384 break; 543 break;
@@ -401,6 +560,36 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
401 "(STUBBED) Attempted to query user exception context address, returned 0"); 560 "(STUBBED) Attempted to query user exception context address, returned 0");
402 *result = 0; 561 *result = 0;
403 break; 562 break;
563 case GetInfoType::ThreadTickCount: {
564 constexpr u64 num_cpus = 4;
565 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
566 return ERR_INVALID_COMBINATION_KERNEL;
567 }
568
569 const auto thread =
570 current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
571 if (!thread) {
572 return ERR_INVALID_HANDLE;
573 }
574
575 const auto& system = Core::System::GetInstance();
576 const auto& scheduler = system.CurrentScheduler();
577 const auto* const current_thread = scheduler.GetCurrentThread();
578 const bool same_thread = current_thread == thread;
579
580 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
581 u64 out_ticks = 0;
582 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
583 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
584
585 out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
586 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
587 out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
588 }
589
590 *result = out_ticks;
591 break;
592 }
404 default: 593 default:
405 UNIMPLEMENTED(); 594 UNIMPLEMENTED();
406 } 595 }
@@ -418,13 +607,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
418static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { 607static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
419 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); 608 LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
420 609
421 auto& kernel = Core::System::GetInstance().Kernel(); 610 const auto* current_process = Core::CurrentProcess();
422 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 611 const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
423 if (!thread) { 612 if (!thread) {
424 return ERR_INVALID_HANDLE; 613 return ERR_INVALID_HANDLE;
425 } 614 }
426 615
427 const auto current_process = Core::CurrentProcess();
428 if (thread->GetOwnerProcess() != current_process) { 616 if (thread->GetOwnerProcess() != current_process) {
429 return ERR_INVALID_HANDLE; 617 return ERR_INVALID_HANDLE;
430 } 618 }
@@ -450,10 +638,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
450 638
451/// Gets the priority for the specified thread 639/// Gets the priority for the specified thread
452static ResultCode GetThreadPriority(u32* priority, Handle handle) { 640static ResultCode GetThreadPriority(u32* priority, Handle handle) {
453 auto& kernel = Core::System::GetInstance().Kernel(); 641 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
454 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); 642 const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
455 if (!thread) 643 if (!thread) {
456 return ERR_INVALID_HANDLE; 644 return ERR_INVALID_HANDLE;
645 }
457 646
458 *priority = thread->GetPriority(); 647 *priority = thread->GetPriority();
459 return RESULT_SUCCESS; 648 return RESULT_SUCCESS;
@@ -465,16 +654,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
465 return ERR_INVALID_THREAD_PRIORITY; 654 return ERR_INVALID_THREAD_PRIORITY;
466 } 655 }
467 656
468 auto& kernel = Core::System::GetInstance().Kernel(); 657 const auto* const current_process = Core::CurrentProcess();
469 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
470 if (!thread)
471 return ERR_INVALID_HANDLE;
472 658
473 // Note: The kernel uses the current process's resource limit instead of 659 // Note: The kernel uses the current process's resource limit instead of
474 // the one from the thread owner's resource limit. 660 // the one from the thread owner's resource limit.
475 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 661 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
476 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 662 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
477 return ERR_NOT_AUTHORIZED; 663 return ERR_INVALID_THREAD_PRIORITY;
664 }
665
666 SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
667 if (!thread) {
668 return ERR_INVALID_HANDLE;
478 } 669 }
479 670
480 thread->SetPriority(priority); 671 thread->SetPriority(priority);
@@ -495,14 +686,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
495 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", 686 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
496 shared_memory_handle, addr, size, permissions); 687 shared_memory_handle, addr, size, permissions);
497 688
498 if (!Is4KBAligned(addr)) { 689 if (!Common::Is4KBAligned(addr)) {
499 return ERR_INVALID_ADDRESS; 690 return ERR_INVALID_ADDRESS;
500 } 691 }
501 692
502 if (size == 0 || !Is4KBAligned(size)) { 693 if (size == 0 || !Common::Is4KBAligned(size)) {
503 return ERR_INVALID_SIZE; 694 return ERR_INVALID_SIZE;
504 } 695 }
505 696
697 if (!IsValidAddressRange(addr, size)) {
698 return ERR_INVALID_ADDRESS_STATE;
699 }
700
506 const auto permissions_type = static_cast<MemoryPermission>(permissions); 701 const auto permissions_type = static_cast<MemoryPermission>(permissions);
507 if (permissions_type != MemoryPermission::Read && 702 if (permissions_type != MemoryPermission::Read &&
508 permissions_type != MemoryPermission::ReadWrite) { 703 permissions_type != MemoryPermission::ReadWrite) {
@@ -510,46 +705,61 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
510 return ERR_INVALID_MEMORY_PERMISSIONS; 705 return ERR_INVALID_MEMORY_PERMISSIONS;
511 } 706 }
512 707
513 auto& kernel = Core::System::GetInstance().Kernel(); 708 auto* const current_process = Core::CurrentProcess();
514 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 709 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
515 if (!shared_memory) { 710 if (!shared_memory) {
516 return ERR_INVALID_HANDLE; 711 return ERR_INVALID_HANDLE;
517 } 712 }
518 713
519 return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, 714 const auto& vm_manager = current_process->VMManager();
520 MemoryPermission::DontCare); 715 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
716 return ERR_INVALID_MEMORY_RANGE;
717 }
718
719 return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
521} 720}
522 721
523static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 722static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
524 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", 723 LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
525 shared_memory_handle, addr, size); 724 shared_memory_handle, addr, size);
526 725
527 if (!Is4KBAligned(addr)) { 726 if (!Common::Is4KBAligned(addr)) {
528 return ERR_INVALID_ADDRESS; 727 return ERR_INVALID_ADDRESS;
529 } 728 }
530 729
531 if (size == 0 || !Is4KBAligned(size)) { 730 if (size == 0 || !Common::Is4KBAligned(size)) {
532 return ERR_INVALID_SIZE; 731 return ERR_INVALID_SIZE;
533 } 732 }
534 733
535 auto& kernel = Core::System::GetInstance().Kernel(); 734 if (!IsValidAddressRange(addr, size)) {
536 auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); 735 return ERR_INVALID_ADDRESS_STATE;
736 }
537 737
538 return shared_memory->Unmap(Core::CurrentProcess().get(), addr); 738 auto* const current_process = Core::CurrentProcess();
739 auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
740 if (!shared_memory) {
741 return ERR_INVALID_HANDLE;
742 }
743
744 const auto& vm_manager = current_process->VMManager();
745 if (!vm_manager.IsWithinASLRRegion(addr, size)) {
746 return ERR_INVALID_MEMORY_RANGE;
747 }
748
749 return shared_memory->Unmap(current_process, addr);
539} 750}
540 751
541/// Query process memory 752/// Query process memory
542static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, 753static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
543 Handle process_handle, u64 addr) { 754 Handle process_handle, u64 addr) {
544 755 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
545 auto& kernel = Core::System::GetInstance().Kernel(); 756 SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
546 SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
547 if (!process) { 757 if (!process) {
548 return ERR_INVALID_HANDLE; 758 return ERR_INVALID_HANDLE;
549 } 759 }
550 auto vma = process->VMManager().FindVMA(addr); 760 auto vma = process->VMManager().FindVMA(addr);
551 memory_info->attributes = 0; 761 memory_info->attributes = 0;
552 if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { 762 if (vma == process->VMManager().vma_map.end()) {
553 memory_info->base_address = 0; 763 memory_info->base_address = 0;
554 memory_info->permission = static_cast<u32>(VMAPermission::None); 764 memory_info->permission = static_cast<u32>(VMAPermission::None);
555 memory_info->size = 0; 765 memory_info->size = 0;
@@ -573,7 +783,7 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
573 783
574/// Exits the current process 784/// Exits the current process
575static void ExitProcess() { 785static void ExitProcess() {
576 auto& current_process = Core::CurrentProcess(); 786 auto* current_process = Core::CurrentProcess();
577 787
578 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); 788 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
579 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, 789 ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
@@ -590,20 +800,19 @@ static void ExitProcess() {
590/// Creates a new thread 800/// Creates a new thread
591static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 801static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
592 u32 priority, s32 processor_id) { 802 u32 priority, s32 processor_id) {
593 std::string name = fmt::format("thread-{:X}", entry_point);
594
595 if (priority > THREADPRIO_LOWEST) { 803 if (priority > THREADPRIO_LOWEST) {
596 return ERR_INVALID_THREAD_PRIORITY; 804 return ERR_INVALID_THREAD_PRIORITY;
597 } 805 }
598 806
599 const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); 807 auto* const current_process = Core::CurrentProcess();
808 const ResourceLimit& resource_limit = current_process->GetResourceLimit();
600 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { 809 if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
601 return ERR_NOT_AUTHORIZED; 810 return ERR_INVALID_THREAD_PRIORITY;
602 } 811 }
603 812
604 if (processor_id == THREADPROCESSORID_DEFAULT) { 813 if (processor_id == THREADPROCESSORID_DEFAULT) {
605 // Set the target CPU to the one specified in the process' exheader. 814 // Set the target CPU to the one specified in the process' exheader.
606 processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); 815 processor_id = current_process->GetDefaultProcessorID();
607 ASSERT(processor_id != THREADPROCESSORID_DEFAULT); 816 ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
608 } 817 }
609 818
@@ -618,11 +827,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
618 return ERR_INVALID_PROCESSOR_ID; 827 return ERR_INVALID_PROCESSOR_ID;
619 } 828 }
620 829
830 const std::string name = fmt::format("thread-{:X}", entry_point);
621 auto& kernel = Core::System::GetInstance().Kernel(); 831 auto& kernel = Core::System::GetInstance().Kernel();
622 CASCADE_RESULT(SharedPtr<Thread> thread, 832 CASCADE_RESULT(SharedPtr<Thread> thread,
623 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, 833 Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
624 Core::CurrentProcess())); 834 *current_process));
625 const auto new_guest_handle = kernel.HandleTable().Create(thread); 835
836 const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
626 if (new_guest_handle.Failed()) { 837 if (new_guest_handle.Failed()) {
627 return new_guest_handle.Code(); 838 return new_guest_handle.Code();
628 } 839 }
@@ -643,8 +854,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
643static ResultCode StartThread(Handle thread_handle) { 854static ResultCode StartThread(Handle thread_handle) {
644 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 855 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
645 856
646 auto& kernel = Core::System::GetInstance().Kernel(); 857 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
647 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 858 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
648 if (!thread) { 859 if (!thread) {
649 return ERR_INVALID_HANDLE; 860 return ERR_INVALID_HANDLE;
650 } 861 }
@@ -691,8 +902,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
691 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", 902 "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
692 mutex_addr, condition_variable_addr, thread_handle, nano_seconds); 903 mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
693 904
694 auto& kernel = Core::System::GetInstance().Kernel(); 905 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
695 SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 906 SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
696 ASSERT(thread); 907 ASSERT(thread);
697 908
698 CASCADE_CODE(Mutex::Release(mutex_addr)); 909 CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -721,7 +932,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
721 std::vector<SharedPtr<Thread>>& waiting_threads, 932 std::vector<SharedPtr<Thread>>& waiting_threads,
722 VAddr condvar_addr) { 933 VAddr condvar_addr) {
723 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 934 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
724 const auto& thread_list = scheduler->GetThreadList(); 935 const auto& thread_list = scheduler.GetThreadList();
725 936
726 for (const auto& thread : thread_list) { 937 for (const auto& thread : thread_list) {
727 if (thread->GetCondVarWaitAddress() == condvar_addr) 938 if (thread->GetCondVarWaitAddress() == condvar_addr)
@@ -803,9 +1014,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
803 mutex_val | Mutex::MutexHasWaitersFlag)); 1014 mutex_val | Mutex::MutexHasWaitersFlag));
804 1015
805 // The mutex is already owned by some other thread, make this thread wait on it. 1016 // The mutex is already owned by some other thread, make this thread wait on it.
806 auto& kernel = Core::System::GetInstance().Kernel(); 1017 const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
807 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); 1018 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
808 auto owner = kernel.HandleTable().Get<Thread>(owner_handle); 1019 auto owner = handle_table.Get<Thread>(owner_handle);
809 ASSERT(owner); 1020 ASSERT(owner);
810 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); 1021 ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
811 thread->InvalidateWakeupCallback(); 1022 thread->InvalidateWakeupCallback();
@@ -884,16 +1095,16 @@ static u64 GetSystemTick() {
884static ResultCode CloseHandle(Handle handle) { 1095static ResultCode CloseHandle(Handle handle) {
885 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); 1096 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
886 1097
887 auto& kernel = Core::System::GetInstance().Kernel(); 1098 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
888 return kernel.HandleTable().Close(handle); 1099 return handle_table.Close(handle);
889} 1100}
890 1101
891/// Reset an event 1102/// Reset an event
892static ResultCode ResetSignal(Handle handle) { 1103static ResultCode ResetSignal(Handle handle) {
893 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle); 1104 LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
894 1105
895 auto& kernel = Core::System::GetInstance().Kernel(); 1106 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
896 auto event = kernel.HandleTable().Get<Event>(handle); 1107 auto event = handle_table.Get<Event>(handle);
897 1108
898 ASSERT(event != nullptr); 1109 ASSERT(event != nullptr);
899 1110
@@ -912,8 +1123,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
912static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { 1123static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
913 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 1124 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
914 1125
915 auto& kernel = Core::System::GetInstance().Kernel(); 1126 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
916 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1127 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
917 if (!thread) { 1128 if (!thread) {
918 return ERR_INVALID_HANDLE; 1129 return ERR_INVALID_HANDLE;
919 } 1130 }
@@ -928,8 +1139,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
928 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle, 1139 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
929 mask, core); 1140 mask, core);
930 1141
931 auto& kernel = Core::System::GetInstance().Kernel(); 1142 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
932 const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle); 1143 const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
933 if (!thread) { 1144 if (!thread) {
934 return ERR_INVALID_HANDLE; 1145 return ERR_INVALID_HANDLE;
935 } 1146 }
@@ -990,7 +1201,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
990 } 1201 }
991 1202
992 auto& kernel = Core::System::GetInstance().Kernel(); 1203 auto& kernel = Core::System::GetInstance().Kernel();
993 auto& handle_table = kernel.HandleTable(); 1204 auto& handle_table = Core::CurrentProcess()->GetHandleTable();
994 auto shared_mem_handle = 1205 auto shared_mem_handle =
995 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, 1206 SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
996 local_perms, remote_perms); 1207 local_perms, remote_perms);
@@ -1002,14 +1213,39 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
1002static ResultCode ClearEvent(Handle handle) { 1213static ResultCode ClearEvent(Handle handle) {
1003 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); 1214 LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
1004 1215
1005 auto& kernel = Core::System::GetInstance().Kernel(); 1216 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1006 SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle); 1217 SharedPtr<Event> evt = handle_table.Get<Event>(handle);
1007 if (evt == nullptr) 1218 if (evt == nullptr) {
1008 return ERR_INVALID_HANDLE; 1219 return ERR_INVALID_HANDLE;
1220 }
1221
1009 evt->Clear(); 1222 evt->Clear();
1010 return RESULT_SUCCESS; 1223 return RESULT_SUCCESS;
1011} 1224}
1012 1225
1226static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
1227 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
1228
1229 // This function currently only allows retrieving a process' status.
1230 enum class InfoType {
1231 Status,
1232 };
1233
1234 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
1235 const auto process = handle_table.Get<Process>(process_handle);
1236 if (!process) {
1237 return ERR_INVALID_HANDLE;
1238 }
1239
1240 const auto info_type = static_cast<InfoType>(type);
1241 if (info_type != InfoType::Status) {
1242 return ERR_INVALID_ENUM_VALUE;
1243 }
1244
1245 *out = static_cast<u64>(process->GetStatus());
1246 return RESULT_SUCCESS;
1247}
1248
1013namespace { 1249namespace {
1014struct FunctionDef { 1250struct FunctionDef {
1015 using Func = void(); 1251 using Func = void();
@@ -1145,7 +1381,7 @@ static const FunctionDef SVC_Table[] = {
1145 {0x79, nullptr, "CreateProcess"}, 1381 {0x79, nullptr, "CreateProcess"},
1146 {0x7A, nullptr, "StartProcess"}, 1382 {0x7A, nullptr, "StartProcess"},
1147 {0x7B, nullptr, "TerminateProcess"}, 1383 {0x7B, nullptr, "TerminateProcess"},
1148 {0x7C, nullptr, "GetProcessInfo"}, 1384 {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
1149 {0x7D, nullptr, "CreateResourceLimit"}, 1385 {0x7D, nullptr, "CreateResourceLimit"},
1150 {0x7E, nullptr, "SetResourceLimitLimitValue"}, 1386 {0x7E, nullptr, "SetResourceLimitLimitValue"},
1151 {0x7F, nullptr, "CallSecureMonitor"}, 1387 {0x7F, nullptr, "CallSecureMonitor"},
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 70148c4fe..b06aac4ec 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -24,37 +24,6 @@ struct PageInfo {
24 u64 flags; 24 u64 flags;
25}; 25};
26 26
27/// Values accepted by svcGetInfo
28enum class GetInfoType : u64 {
29 // 1.0.0+
30 AllowedCpuIdBitmask = 0,
31 AllowedThreadPrioBitmask = 1,
32 MapRegionBaseAddr = 2,
33 MapRegionSize = 3,
34 HeapRegionBaseAddr = 4,
35 HeapRegionSize = 5,
36 TotalMemoryUsage = 6,
37 TotalHeapUsage = 7,
38 IsCurrentProcessBeingDebugged = 8,
39 ResourceHandleLimit = 9,
40 IdleTickCount = 10,
41 RandomEntropy = 11,
42 PerformanceCounter = 0xF0000002,
43 // 2.0.0+
44 AddressSpaceBaseAddr = 12,
45 AddressSpaceSize = 13,
46 NewMapRegionBaseAddr = 14,
47 NewMapRegionSize = 15,
48 // 3.0.0+
49 IsVirtualAddressMemoryEnabled = 16,
50 PersonalMmHeapUsage = 17,
51 TitleId = 18,
52 // 4.0.0+
53 PrivilegedProcessId = 19,
54 // 5.0.0+
55 UserExceptionContextAddr = 20,
56};
57
58void CallSVC(u32 immediate); 27void CallSVC(u32 immediate);
59 28
60} // namespace Kernel 29} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 22712e64f..b09753c80 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -35,18 +35,18 @@ void SvcWrap() {
35 35
36template <ResultCode func(u32)> 36template <ResultCode func(u32)>
37void SvcWrap() { 37void SvcWrap() {
38 FuncReturn(func((u32)Param(0)).raw); 38 FuncReturn(func(static_cast<u32>(Param(0))).raw);
39} 39}
40 40
41template <ResultCode func(u32, u32)> 41template <ResultCode func(u32, u32)>
42void SvcWrap() { 42void SvcWrap() {
43 FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); 43 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
44} 44}
45 45
46template <ResultCode func(u32*, u32)> 46template <ResultCode func(u32*, u32)>
47void SvcWrap() { 47void SvcWrap() {
48 u32 param_1 = 0; 48 u32 param_1 = 0;
49 u32 retval = func(&param_1, (u32)Param(1)).raw; 49 u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
50 Core::CurrentArmInterface().SetReg(1, param_1); 50 Core::CurrentArmInterface().SetReg(1, param_1);
51 FuncReturn(retval); 51 FuncReturn(retval);
52} 52}
@@ -61,7 +61,7 @@ void SvcWrap() {
61 61
62template <ResultCode func(u64, s32)> 62template <ResultCode func(u64, s32)>
63void SvcWrap() { 63void SvcWrap() {
64 FuncReturn(func(Param(0), (s32)Param(1)).raw); 64 FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
65} 65}
66 66
67template <ResultCode func(u64, u32)> 67template <ResultCode func(u64, u32)>
@@ -77,21 +77,29 @@ void SvcWrap() {
77 FuncReturn(retval); 77 FuncReturn(retval);
78} 78}
79 79
80template <ResultCode func(u64*, u32, u32)>
81void SvcWrap() {
82 u64 param_1 = 0;
83 u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
84 Core::CurrentArmInterface().SetReg(1, param_1);
85 FuncReturn(retval);
86}
87
80template <ResultCode func(u32, u64)> 88template <ResultCode func(u32, u64)>
81void SvcWrap() { 89void SvcWrap() {
82 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); 90 FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
83} 91}
84 92
85template <ResultCode func(u32, u32, u64)> 93template <ResultCode func(u32, u32, u64)>
86void SvcWrap() { 94void SvcWrap() {
87 FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); 95 FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
88} 96}
89 97
90template <ResultCode func(u32, u32*, u64*)> 98template <ResultCode func(u32, u32*, u64*)>
91void SvcWrap() { 99void SvcWrap() {
92 u32 param_1 = 0; 100 u32 param_1 = 0;
93 u64 param_2 = 0; 101 u64 param_2 = 0;
94 ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2); 102 ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
95 Core::CurrentArmInterface().SetReg(1, param_1); 103 Core::CurrentArmInterface().SetReg(1, param_1);
96 Core::CurrentArmInterface().SetReg(2, param_2); 104 Core::CurrentArmInterface().SetReg(2, param_2);
97 FuncReturn(retval.raw); 105 FuncReturn(retval.raw);
@@ -100,12 +108,12 @@ void SvcWrap() {
100template <ResultCode func(u64, u64, u32, u32)> 108template <ResultCode func(u64, u64, u32, u32)>
101void SvcWrap() { 109void SvcWrap() {
102 FuncReturn( 110 FuncReturn(
103 func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); 111 func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
104} 112}
105 113
106template <ResultCode func(u32, u64, u32)> 114template <ResultCode func(u32, u64, u32)>
107void SvcWrap() { 115void SvcWrap() {
108 FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); 116 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
109} 117}
110 118
111template <ResultCode func(u64, u64, u64)> 119template <ResultCode func(u64, u64, u64)>
@@ -115,25 +123,28 @@ void SvcWrap() {
115 123
116template <ResultCode func(u32, u64, u64, u32)> 124template <ResultCode func(u32, u64, u64, u32)>
117void SvcWrap() { 125void SvcWrap() {
118 FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); 126 FuncReturn(
127 func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
119} 128}
120 129
121template <ResultCode func(u32, u64, u64)> 130template <ResultCode func(u32, u64, u64)>
122void SvcWrap() { 131void SvcWrap() {
123 FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); 132 FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
124} 133}
125 134
126template <ResultCode func(u32*, u64, u64, s64)> 135template <ResultCode func(u32*, u64, u64, s64)>
127void SvcWrap() { 136void SvcWrap() {
128 u32 param_1 = 0; 137 u32 param_1 = 0;
129 ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); 138 ResultCode retval =
139 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
130 Core::CurrentArmInterface().SetReg(1, param_1); 140 Core::CurrentArmInterface().SetReg(1, param_1);
131 FuncReturn(retval.raw); 141 FuncReturn(retval.raw);
132} 142}
133 143
134template <ResultCode func(u64, u64, u32, s64)> 144template <ResultCode func(u64, u64, u32, s64)>
135void SvcWrap() { 145void SvcWrap() {
136 FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); 146 FuncReturn(
147 func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
137} 148}
138 149
139template <ResultCode func(u64*, u64, u64, u64)> 150template <ResultCode func(u64*, u64, u64, u64)>
@@ -147,9 +158,9 @@ void SvcWrap() {
147template <ResultCode func(u32*, u64, u64, u64, u32, s32)> 158template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
148void SvcWrap() { 159void SvcWrap() {
149 u32 param_1 = 0; 160 u32 param_1 = 0;
150 u32 retval = 161 u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
151 func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) 162 static_cast<s32>(Param(5)))
152 .raw; 163 .raw;
153 Core::CurrentArmInterface().SetReg(1, param_1); 164 Core::CurrentArmInterface().SetReg(1, param_1);
154 FuncReturn(retval); 165 FuncReturn(retval);
155} 166}
@@ -172,7 +183,7 @@ void SvcWrap() {
172template <ResultCode func(u32*, u64, u64, u32)> 183template <ResultCode func(u32*, u64, u64, u32)>
173void SvcWrap() { 184void SvcWrap() {
174 u32 param_1 = 0; 185 u32 param_1 = 0;
175 u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; 186 u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
176 Core::CurrentArmInterface().SetReg(1, param_1); 187 Core::CurrentArmInterface().SetReg(1, param_1);
177 FuncReturn(retval); 188 FuncReturn(retval);
178} 189}
@@ -181,22 +192,22 @@ template <ResultCode func(Handle*, u64, u32, u32)>
181void SvcWrap() { 192void SvcWrap() {
182 u32 param_1 = 0; 193 u32 param_1 = 0;
183 u32 retval = 194 u32 retval =
184 func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; 195 func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
185 Core::CurrentArmInterface().SetReg(1, param_1); 196 Core::CurrentArmInterface().SetReg(1, param_1);
186 FuncReturn(retval); 197 FuncReturn(retval);
187} 198}
188 199
189template <ResultCode func(u64, u32, s32, s64)> 200template <ResultCode func(u64, u32, s32, s64)>
190void SvcWrap() { 201void SvcWrap() {
191 FuncReturn( 202 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
192 func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) 203 static_cast<s64>(Param(3)))
193 .raw); 204 .raw);
194} 205}
195 206
196template <ResultCode func(u64, u32, s32, s32)> 207template <ResultCode func(u64, u32, s32, s32)>
197void SvcWrap() { 208void SvcWrap() {
198 FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), 209 FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
199 (s32)(Param(3) & 0xFFFFFFFF)) 210 static_cast<s32>(Param(3)))
200 .raw); 211 .raw);
201} 212}
202 213
@@ -226,7 +237,7 @@ void SvcWrap() {
226 237
227template <void func(s64)> 238template <void func(s64)>
228void SvcWrap() { 239void SvcWrap() {
229 func((s64)Param(0)); 240 func(static_cast<s64>(Param(0)));
230} 241}
231 242
232template <void func(u64, u64 len)> 243template <void func(u64, u64 len)>
@@ -239,4 +250,9 @@ void SvcWrap() {
239 func(Param(0), Param(1), Param(2)); 250 func(Param(0), Param(1), Param(2));
240} 251}
241 252
253template <void func(u32, u64, u64)>
254void SvcWrap() {
255 func(static_cast<u32>(Param(0)), Param(1), Param(2));
256}
257
242} // namespace Kernel 258} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 8e514cf9a..dd5cd9ced 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -4,9 +4,9 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes> 6#include <cinttypes>
7#include <optional>
7#include <vector> 8#include <vector>
8 9
9#include <boost/optional.hpp>
10#include <boost/range/algorithm_ext/erase.hpp> 10#include <boost/range/algorithm_ext/erase.hpp>
11 11
12#include "common/assert.h" 12#include "common/assert.h"
@@ -94,10 +94,10 @@ void Thread::CancelWakeupTimer() {
94 CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle); 94 CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
95} 95}
96 96
97static boost::optional<s32> GetNextProcessorId(u64 mask) { 97static std::optional<s32> GetNextProcessorId(u64 mask) {
98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { 98 for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
99 if (mask & (1ULL << index)) { 99 if (mask & (1ULL << index)) {
100 if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) { 100 if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
101 // Core is enabled and not running any threads, use this one 101 // Core is enabled and not running any threads, use this one
102 return index; 102 return index;
103 } 103 }
@@ -142,19 +142,19 @@ void Thread::ResumeFromWait() {
142 142
143 status = ThreadStatus::Ready; 143 status = ThreadStatus::Ready;
144 144
145 boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); 145 std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
146 if (!new_processor_id) { 146 if (!new_processor_id) {
147 new_processor_id = processor_id; 147 new_processor_id = processor_id;
148 } 148 }
149 if (ideal_core != -1 && 149 if (ideal_core != -1 &&
150 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 150 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
151 new_processor_id = ideal_core; 151 new_processor_id = ideal_core;
152 } 152 }
153 153
154 ASSERT(*new_processor_id < 4); 154 ASSERT(*new_processor_id < 4);
155 155
156 // Add thread to new core's scheduler 156 // Add thread to new core's scheduler
157 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 157 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
158 158
159 if (*new_processor_id != processor_id) { 159 if (*new_processor_id != processor_id) {
160 // Remove thread from previous core's scheduler 160 // Remove thread from previous core's scheduler
@@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {
169 next_scheduler->ScheduleThread(this, current_priority); 169 next_scheduler->ScheduleThread(this, current_priority);
170 170
171 // Change thread's scheduler 171 // Change thread's scheduler
172 scheduler = next_scheduler.get(); 172 scheduler = next_scheduler;
173 173
174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 174 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
175} 175}
@@ -183,18 +183,15 @@ void Thread::ResumeFromWait() {
183 */ 183 */
184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, 184static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
185 VAddr entry_point, u64 arg) { 185 VAddr entry_point, u64 arg) {
186 memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext)); 186 context = {};
187
188 context.cpu_registers[0] = arg; 187 context.cpu_registers[0] = arg;
189 context.pc = entry_point; 188 context.pc = entry_point;
190 context.sp = stack_top; 189 context.sp = stack_top;
191 context.pstate = 0;
192 context.fpcr = 0;
193} 190}
194 191
195ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, 192ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
196 u32 priority, u64 arg, s32 processor_id, 193 u32 priority, u64 arg, s32 processor_id,
197 VAddr stack_top, SharedPtr<Process> owner_process) { 194 VAddr stack_top, Process& owner_process) {
198 // Check if priority is in ranged. Lowest priority -> highest priority id. 195 // Check if priority is in ranged. Lowest priority -> highest priority id.
199 if (priority > THREADPRIO_LOWEST) { 196 if (priority > THREADPRIO_LOWEST) {
200 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 197 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -208,7 +205,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
208 205
209 // TODO(yuriks): Other checks, returning 0xD9001BEA 206 // TODO(yuriks): Other checks, returning 0xD9001BEA
210 207
211 if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { 208 if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
212 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 209 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
213 // TODO (bunnei): Find the correct error code to use here 210 // TODO (bunnei): Find the correct error code to use here
214 return ResultCode(-1); 211 return ResultCode(-1);
@@ -232,8 +229,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
232 thread->wait_handle = 0; 229 thread->wait_handle = 0;
233 thread->name = std::move(name); 230 thread->name = std::move(name);
234 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 231 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
235 thread->owner_process = owner_process; 232 thread->owner_process = &owner_process;
236 thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); 233 thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
237 thread->scheduler->AddThread(thread, priority); 234 thread->scheduler->AddThread(thread, priority);
238 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); 235 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
239 236
@@ -264,12 +261,12 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
264 // Initialize new "main" thread 261 // Initialize new "main" thread
265 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); 262 const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
266 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, 263 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
267 stack_top, &owner_process); 264 stack_top, owner_process);
268 265
269 SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); 266 SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
270 267
271 // Register 1 must be a handle to the main thread 268 // Register 1 must be a handle to the main thread
272 const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap(); 269 const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
273 thread->SetGuestHandle(guest_handle); 270 thread->SetGuestHandle(guest_handle);
274 thread->GetContext().cpu_registers[1] = guest_handle; 271 thread->GetContext().cpu_registers[1] = guest_handle;
275 272
@@ -372,20 +369,20 @@ void Thread::ChangeCore(u32 core, u64 mask) {
372 return; 369 return;
373 } 370 }
374 371
375 boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; 372 std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
376 373
377 if (!new_processor_id) { 374 if (!new_processor_id) {
378 new_processor_id = processor_id; 375 new_processor_id = processor_id;
379 } 376 }
380 if (ideal_core != -1 && 377 if (ideal_core != -1 &&
381 Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { 378 Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
382 new_processor_id = ideal_core; 379 new_processor_id = ideal_core;
383 } 380 }
384 381
385 ASSERT(*new_processor_id < 4); 382 ASSERT(*new_processor_id < 4);
386 383
387 // Add thread to new core's scheduler 384 // Add thread to new core's scheduler
388 auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); 385 auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
389 386
390 if (*new_processor_id != processor_id) { 387 if (*new_processor_id != processor_id) {
391 // Remove thread from previous core's scheduler 388 // Remove thread from previous core's scheduler
@@ -400,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
400 next_scheduler->ScheduleThread(this, current_priority); 397 next_scheduler->ScheduleThread(this, current_priority);
401 398
402 // Change thread's scheduler 399 // Change thread's scheduler
403 scheduler = next_scheduler.get(); 400 scheduler = next_scheduler;
404 401
405 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); 402 Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
406} 403}
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c6ffbd28c..4a6e11239 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -89,7 +89,7 @@ public:
89 static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name, 89 static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name,
90 VAddr entry_point, u32 priority, u64 arg, 90 VAddr entry_point, u32 priority, u64 arg,
91 s32 processor_id, VAddr stack_top, 91 s32 processor_id, VAddr stack_top,
92 SharedPtr<Process> owner_process); 92 Process& owner_process);
93 93
94 std::string GetName() const override { 94 std::string GetName() const override {
95 return name; 95 return name;
@@ -258,15 +258,23 @@ public:
258 return last_running_ticks; 258 return last_running_ticks;
259 } 259 }
260 260
261 u64 GetTotalCPUTimeTicks() const {
262 return total_cpu_time_ticks;
263 }
264
265 void UpdateCPUTimeTicks(u64 ticks) {
266 total_cpu_time_ticks += ticks;
267 }
268
261 s32 GetProcessorID() const { 269 s32 GetProcessorID() const {
262 return processor_id; 270 return processor_id;
263 } 271 }
264 272
265 SharedPtr<Process>& GetOwnerProcess() { 273 Process* GetOwnerProcess() {
266 return owner_process; 274 return owner_process;
267 } 275 }
268 276
269 const SharedPtr<Process>& GetOwnerProcess() const { 277 const Process* GetOwnerProcess() const {
270 return owner_process; 278 return owner_process;
271 } 279 }
272 280
@@ -378,7 +386,8 @@ private:
378 u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application 386 u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
379 u32 current_priority = 0; ///< Current thread priority, can be temporarily changed 387 u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
380 388
381 u64 last_running_ticks = 0; ///< CPU tick when thread was last running 389 u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
390 u64 last_running_ticks = 0; ///< CPU tick when thread was last running
382 391
383 s32 processor_id = 0; 392 s32 processor_id = 0;
384 393
@@ -386,7 +395,7 @@ private:
386 u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. 395 u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
387 396
388 /// Process that owns this thread 397 /// Process that owns this thread
389 SharedPtr<Process> owner_process; 398 Process* owner_process;
390 399
391 /// Objects that the thread is waiting on, in the same order as they were 400 /// Objects that the thread is waiting on, in the same order as they were
392 /// passed to WaitSynchronization1/N. 401 /// passed to WaitSynchronization1/N.
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index e412309fd..1a92c8f70 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
143 return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); 143 return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
144} 144}
145 145
146ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
147 // Find the first Free VMA.
148 const VAddr base = GetASLRRegionBaseAddress();
149 const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
150 if (vma.second.type != VMAType::Free)
151 return false;
152
153 const VAddr vma_end = vma.second.base + vma.second.size;
154 return vma_end > base && vma_end >= base + size;
155 });
156
157 if (vma_handle == vma_map.end()) {
158 // TODO(Subv): Find the correct error code here.
159 return ResultCode(-1);
160 }
161
162 const VAddr target = std::max(base, vma_handle->second.base);
163 return MakeResult<VAddr>(target);
164}
165
146ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, 166ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
147 MemoryState state, 167 MemoryState state,
148 Memory::MemoryHookPointer mmio_handler) { 168 Memory::MemoryHookPointer mmio_handler) {
@@ -393,30 +413,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
393 413
394 switch (type) { 414 switch (type) {
395 case FileSys::ProgramAddressSpaceType::Is32Bit: 415 case FileSys::ProgramAddressSpaceType::Is32Bit:
416 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
396 address_space_width = 32; 417 address_space_width = 32;
397 code_region_base = 0x200000; 418 code_region_base = 0x200000;
398 code_region_end = code_region_base + 0x3FE00000; 419 code_region_end = code_region_base + 0x3FE00000;
399 map_region_size = 0x40000000; 420 aslr_region_base = 0x200000;
400 heap_region_size = 0x40000000; 421 aslr_region_end = aslr_region_base + 0xFFE00000;
422 if (type == FileSys::ProgramAddressSpaceType::Is32Bit) {
423 map_region_size = 0x40000000;
424 heap_region_size = 0x40000000;
425 } else {
426 map_region_size = 0;
427 heap_region_size = 0x80000000;
428 }
401 break; 429 break;
402 case FileSys::ProgramAddressSpaceType::Is36Bit: 430 case FileSys::ProgramAddressSpaceType::Is36Bit:
403 address_space_width = 36; 431 address_space_width = 36;
404 code_region_base = 0x8000000; 432 code_region_base = 0x8000000;
405 code_region_end = code_region_base + 0x78000000; 433 code_region_end = code_region_base + 0x78000000;
434 aslr_region_base = 0x8000000;
435 aslr_region_end = aslr_region_base + 0xFF8000000;
406 map_region_size = 0x180000000; 436 map_region_size = 0x180000000;
407 heap_region_size = 0x180000000; 437 heap_region_size = 0x180000000;
408 break; 438 break;
409 case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
410 address_space_width = 32;
411 code_region_base = 0x200000;
412 code_region_end = code_region_base + 0x3FE00000;
413 map_region_size = 0;
414 heap_region_size = 0x80000000;
415 break;
416 case FileSys::ProgramAddressSpaceType::Is39Bit: 439 case FileSys::ProgramAddressSpaceType::Is39Bit:
417 address_space_width = 39; 440 address_space_width = 39;
418 code_region_base = 0x8000000; 441 code_region_base = 0x8000000;
419 code_region_end = code_region_base + 0x80000000; 442 code_region_end = code_region_base + 0x80000000;
443 aslr_region_base = 0x8000000;
444 aslr_region_end = aslr_region_base + 0x7FF8000000;
420 map_region_size = 0x1000000000; 445 map_region_size = 0x1000000000;
421 heap_region_size = 0x180000000; 446 heap_region_size = 0x180000000;
422 new_map_region_size = 0x80000000; 447 new_map_region_size = 0x80000000;
@@ -490,6 +515,38 @@ u64 VMManager::GetAddressSpaceWidth() const {
490 return address_space_width; 515 return address_space_width;
491} 516}
492 517
518VAddr VMManager::GetASLRRegionBaseAddress() const {
519 return aslr_region_base;
520}
521
522VAddr VMManager::GetASLRRegionEndAddress() const {
523 return aslr_region_end;
524}
525
526u64 VMManager::GetASLRRegionSize() const {
527 return aslr_region_end - aslr_region_base;
528}
529
530bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
531 const VAddr range_end = begin + size;
532 const VAddr aslr_start = GetASLRRegionBaseAddress();
533 const VAddr aslr_end = GetASLRRegionEndAddress();
534
535 if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
536 return false;
537 }
538
539 if (range_end > heap_region_base && heap_region_end > begin) {
540 return false;
541 }
542
543 if (range_end > map_region_base && map_region_end > begin) {
544 return false;
545 }
546
547 return true;
548}
549
493VAddr VMManager::GetCodeRegionBaseAddress() const { 550VAddr VMManager::GetCodeRegionBaseAddress() const {
494 return code_region_base; 551 return code_region_base;
495} 552}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 015559a64..2447cbb8f 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -158,6 +158,14 @@ public:
158 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); 158 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
159 159
160 /** 160 /**
161 * Finds the first free address that can hold a region of the desired size.
162 *
163 * @param size Size of the desired region.
164 * @return The found free address.
165 */
166 ResultVal<VAddr> FindFreeRegion(u64 size) const;
167
168 /**
161 * Maps a memory-mapped IO region at a given address. 169 * Maps a memory-mapped IO region at a given address.
162 * 170 *
163 * @param target The guest address to start the mapping at. 171 * @param target The guest address to start the mapping at.
@@ -205,6 +213,18 @@ public:
205 /// Gets the address space width in bits. 213 /// Gets the address space width in bits.
206 u64 GetAddressSpaceWidth() const; 214 u64 GetAddressSpaceWidth() const;
207 215
216 /// Gets the base address of the ASLR region.
217 VAddr GetASLRRegionBaseAddress() const;
218
219 /// Gets the end address of the ASLR region.
220 VAddr GetASLRRegionEndAddress() const;
221
222 /// Determines whether or not the specified address range is within the ASLR region.
223 bool IsWithinASLRRegion(VAddr address, u64 size) const;
224
225 /// Gets the size of the ASLR region
226 u64 GetASLRRegionSize() const;
227
208 /// Gets the base address of the code region. 228 /// Gets the base address of the code region.
209 VAddr GetCodeRegionBaseAddress() const; 229 VAddr GetCodeRegionBaseAddress() const;
210 230
@@ -306,6 +326,9 @@ private:
306 VAddr address_space_base = 0; 326 VAddr address_space_base = 0;
307 VAddr address_space_end = 0; 327 VAddr address_space_end = 0;
308 328
329 VAddr aslr_region_base = 0;
330 VAddr aslr_region_end = 0;
331
309 VAddr code_region_base = 0; 332 VAddr code_region_base = 0;
310 VAddr code_region_end = 0; 333 VAddr code_region_end = 0;
311 334
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e61748ca3..c6437a671 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -2,9 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <array> 6#include <array>
7#include "common/common_paths.h"
6#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/file_util.h"
7#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "common/string_util.h"
8#include "common/swap.h" 12#include "common/swap.h"
9#include "core/core_timing.h" 13#include "core/core_timing.h"
10#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
@@ -16,6 +20,7 @@
16#include "core/hle/service/acc/profile_manager.h" 20#include "core/hle/service/acc/profile_manager.h"
17 21
18namespace Service::Account { 22namespace Service::Account {
23
19// TODO: RE this structure 24// TODO: RE this structure
20struct UserData { 25struct UserData {
21 INSERT_PADDING_WORDS(1); 26 INSERT_PADDING_WORDS(1);
@@ -27,6 +32,29 @@ struct UserData {
27}; 32};
28static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); 33static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
29 34
35// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
36// used as a backup should the one on disk not exist
37constexpr u32 backup_jpeg_size = 107;
38constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
39 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
40 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
41 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
42 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
43 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
44 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
45 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
46}};
47
48static std::string GetImagePath(UUID uuid) {
49 return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
50 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
51}
52
53static constexpr u32 SanitizeJPEGSize(std::size_t size) {
54 constexpr std::size_t max_jpeg_image_size = 0x20000;
55 return static_cast<u32>(std::min(size, max_jpeg_image_size));
56}
57
30class IProfile final : public ServiceFramework<IProfile> { 58class IProfile final : public ServiceFramework<IProfile> {
31public: 59public:
32 explicit IProfile(UUID user_id, ProfileManager& profile_manager) 60 explicit IProfile(UUID user_id, ProfileManager& profile_manager)
@@ -73,32 +101,42 @@ private:
73 } 101 }
74 102
75 void LoadImage(Kernel::HLERequestContext& ctx) { 103 void LoadImage(Kernel::HLERequestContext& ctx) {
76 LOG_WARNING(Service_ACC, "(STUBBED) called"); 104 LOG_DEBUG(Service_ACC, "called");
77 // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg 105
78 // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
79 constexpr u32 jpeg_size = 107;
80 static constexpr std::array<u8, jpeg_size> jpeg{
81 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
82 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
83 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
84 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
85 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
86 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
87 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
88 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
89 };
90 ctx.WriteBuffer(jpeg);
91 IPC::ResponseBuilder rb{ctx, 3}; 106 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(RESULT_SUCCESS); 107 rb.Push(RESULT_SUCCESS);
93 rb.Push<u32>(jpeg_size); 108
109 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
110 if (!image.IsOpen()) {
111 LOG_WARNING(Service_ACC,
112 "Failed to load user provided image! Falling back to built-in backup...");
113 ctx.WriteBuffer(backup_jpeg);
114 rb.Push<u32>(backup_jpeg_size);
115 return;
116 }
117
118 const u32 size = SanitizeJPEGSize(image.GetSize());
119 std::vector<u8> buffer(size);
120 image.ReadBytes(buffer.data(), buffer.size());
121
122 ctx.WriteBuffer(buffer.data(), buffer.size());
123 rb.Push<u32>(size);
94 } 124 }
95 125
96 void GetImageSize(Kernel::HLERequestContext& ctx) { 126 void GetImageSize(Kernel::HLERequestContext& ctx) {
97 LOG_WARNING(Service_ACC, "(STUBBED) called"); 127 LOG_DEBUG(Service_ACC, "called");
98 constexpr u32 jpeg_size = 107;
99 IPC::ResponseBuilder rb{ctx, 3}; 128 IPC::ResponseBuilder rb{ctx, 3};
100 rb.Push(RESULT_SUCCESS); 129 rb.Push(RESULT_SUCCESS);
101 rb.Push<u32>(jpeg_size); 130
131 const FileUtil::IOFile image(GetImagePath(user_id), "rb");
132
133 if (!image.IsOpen()) {
134 LOG_WARNING(Service_ACC,
135 "Failed to load user provided image! Falling back to built-in backup...");
136 rb.Push<u32>(backup_jpeg_size);
137 } else {
138 rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
139 }
102 } 140 }
103 141
104 const ProfileManager& profile_manager; 142 const ProfileManager& profile_manager;
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index bcb3475db..c08394e4c 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -3,41 +3,66 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <random> 5#include <random>
6#include <boost/optional.hpp> 6
7#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h" 8#include "core/hle/service/acc/profile_manager.h"
8#include "core/settings.h" 9#include "core/settings.h"
9 10
10namespace Service::Account { 11namespace Service::Account {
12
13struct UserRaw {
14 UUID uuid;
15 UUID uuid2;
16 u64 timestamp;
17 ProfileUsername username;
18 INSERT_PADDING_BYTES(0x80);
19};
20static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
21
22struct ProfileDataRaw {
23 INSERT_PADDING_BYTES(0x10);
24 std::array<UserRaw, MAX_USERS> users;
25};
26static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
27
11// TODO(ogniK): Get actual error codes 28// TODO(ogniK): Get actual error codes
12constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); 29constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
13constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); 30constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
14constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); 31constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
15 32
16const UUID& UUID::Generate() { 33constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
34
35UUID UUID::Generate() {
17 std::random_device device; 36 std::random_device device;
18 std::mt19937 gen(device()); 37 std::mt19937 gen(device());
19 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 38 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
20 uuid[0] = distribution(gen); 39 return UUID{distribution(gen), distribution(gen)};
21 uuid[1] = distribution(gen);
22 return *this;
23} 40}
24 41
25ProfileManager::ProfileManager() { 42ProfileManager::ProfileManager() {
26 // TODO(ogniK): Create the default user we have for now until loading/saving users is added 43 ParseUserSaveFile();
27 auto user_uuid = UUID{1, 0}; 44
28 ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); 45 if (user_count == 0)
29 OpenUser(user_uuid); 46 CreateNewUser(UUID::Generate(), "yuzu");
47
48 auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
49 if (UserExistsIndex(current))
50 current = 0;
51
52 OpenUser(*GetUser(current));
30} 53}
31 54
32ProfileManager::~ProfileManager() = default; 55ProfileManager::~ProfileManager() {
56 WriteUserSaveFile();
57}
33 58
34/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the 59/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
35/// internal management of the users profiles 60/// internal management of the users profiles
36boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { 61std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
37 if (user_count >= MAX_USERS) { 62 if (user_count >= MAX_USERS) {
38 return boost::none; 63 return {};
39 } 64 }
40 profiles[user_count] = user; 65 profiles[user_count] = profile;
41 return user_count++; 66 return user_count++;
42} 67}
43 68
@@ -56,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
56 81
57/// Helper function to register a user to the system 82/// Helper function to register a user to the system
58ResultCode ProfileManager::AddUser(const ProfileInfo& user) { 83ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
59 if (AddToProfiles(user) == boost::none) { 84 if (!AddToProfiles(user)) {
60 return ERROR_TOO_MANY_USERS; 85 return ERROR_TOO_MANY_USERS;
61 } 86 }
62 return RESULT_SUCCESS; 87 return RESULT_SUCCESS;
@@ -101,31 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
101 return CreateNewUser(uuid, username_output); 126 return CreateNewUser(uuid, username_output);
102} 127}
103 128
129std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
130 if (index >= MAX_USERS) {
131 return {};
132 }
133
134 return profiles[index].user_uuid;
135}
136
104/// Returns a users profile index based on their user id. 137/// Returns a users profile index based on their user id.
105boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 138std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
106 if (!uuid) { 139 if (!uuid) {
107 return boost::none; 140 return {};
108 } 141 }
109 auto iter = std::find_if(profiles.begin(), profiles.end(), 142
110 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); 143 const auto iter = std::find_if(profiles.begin(), profiles.end(),
144 [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
111 if (iter == profiles.end()) { 145 if (iter == profiles.end()) {
112 return boost::none; 146 return {};
113 } 147 }
148
114 return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); 149 return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
115} 150}
116 151
117/// Returns a users profile index based on their profile 152/// Returns a users profile index based on their profile
118boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { 153std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
119 return GetUserIndex(user.user_uuid); 154 return GetUserIndex(user.user_uuid);
120} 155}
121 156
122/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 157/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
123bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, 158bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
124 ProfileBase& profile) const { 159 if (!index || index >= MAX_USERS) {
125 if (index == boost::none || index >= MAX_USERS) {
126 return false; 160 return false;
127 } 161 }
128 const auto& prof_info = profiles[index.get()]; 162 const auto& prof_info = profiles[*index];
129 profile.user_uuid = prof_info.user_uuid; 163 profile.user_uuid = prof_info.user_uuid;
130 profile.username = prof_info.username; 164 profile.username = prof_info.username;
131 profile.timestamp = prof_info.creation_time; 165 profile.timestamp = prof_info.creation_time;
@@ -134,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
134 168
135/// Returns the data structure used by the switch when GetProfileBase is called on acc:* 169/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
136bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { 170bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
137 auto idx = GetUserIndex(uuid); 171 const auto idx = GetUserIndex(uuid);
138 return GetProfileBase(idx, profile); 172 return GetProfileBase(idx, profile);
139} 173}
140 174
@@ -161,26 +195,34 @@ std::size_t ProfileManager::GetOpenUserCount() const {
161 195
162/// Checks if a user id exists in our profile manager 196/// Checks if a user id exists in our profile manager
163bool ProfileManager::UserExists(UUID uuid) const { 197bool ProfileManager::UserExists(UUID uuid) const {
164 return (GetUserIndex(uuid) != boost::none); 198 return GetUserIndex(uuid).has_value();
199}
200
201bool ProfileManager::UserExistsIndex(std::size_t index) const {
202 if (index >= MAX_USERS)
203 return false;
204 return profiles[index].user_uuid.uuid != INVALID_UUID;
165} 205}
166 206
167/// Opens a specific user 207/// Opens a specific user
168void ProfileManager::OpenUser(UUID uuid) { 208void ProfileManager::OpenUser(UUID uuid) {
169 auto idx = GetUserIndex(uuid); 209 const auto idx = GetUserIndex(uuid);
170 if (idx == boost::none) { 210 if (!idx) {
171 return; 211 return;
172 } 212 }
173 profiles[idx.get()].is_open = true; 213
214 profiles[*idx].is_open = true;
174 last_opened_user = uuid; 215 last_opened_user = uuid;
175} 216}
176 217
177/// Closes a specific user 218/// Closes a specific user
178void ProfileManager::CloseUser(UUID uuid) { 219void ProfileManager::CloseUser(UUID uuid) {
179 auto idx = GetUserIndex(uuid); 220 const auto idx = GetUserIndex(uuid);
180 if (idx == boost::none) { 221 if (!idx) {
181 return; 222 return;
182 } 223 }
183 profiles[idx.get()].is_open = false; 224
225 profiles[*idx].is_open = false;
184} 226}
185 227
186/// Gets all valid user ids on the system 228/// Gets all valid user ids on the system
@@ -210,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
210} 252}
211 253
212/// Return the users profile base and the unknown arbitary data. 254/// Return the users profile base and the unknown arbitary data.
213bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, 255bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
214 ProfileData& data) const { 256 ProfileData& data) const {
215 if (GetProfileBase(index, profile)) { 257 if (GetProfileBase(index, profile)) {
216 data = profiles[index.get()].data; 258 data = profiles[*index].data;
217 return true; 259 return true;
218 } 260 }
219 return false; 261 return false;
@@ -222,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P
222/// Return the users profile base and the unknown arbitary data. 264/// Return the users profile base and the unknown arbitary data.
223bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, 265bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
224 ProfileData& data) const { 266 ProfileData& data) const {
225 auto idx = GetUserIndex(uuid); 267 const auto idx = GetUserIndex(uuid);
226 return GetProfileBaseAndData(idx, profile, data); 268 return GetProfileBaseAndData(idx, profile, data);
227} 269}
228 270
@@ -239,4 +281,96 @@ bool ProfileManager::CanSystemRegisterUser() const {
239 // emulate qlaunch. Update this to dynamically change. 281 // emulate qlaunch. Update this to dynamically change.
240} 282}
241 283
284bool ProfileManager::RemoveUser(UUID uuid) {
285 const auto index = GetUserIndex(uuid);
286 if (!index) {
287 return false;
288 }
289
290 profiles[*index] = ProfileInfo{};
291 std::stable_partition(profiles.begin(), profiles.end(),
292 [](const ProfileInfo& profile) { return profile.user_uuid; });
293 return true;
294}
295
296bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
297 const auto index = GetUserIndex(uuid);
298 if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
299 return false;
300 }
301
302 auto& profile = profiles[*index];
303 profile.user_uuid = profile_new.user_uuid;
304 profile.username = profile_new.username;
305 profile.creation_time = profile_new.timestamp;
306
307 return true;
308}
309
310void ProfileManager::ParseUserSaveFile() {
311 FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
312 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
313 "rb");
314
315 if (!save.IsOpen()) {
316 LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
317 "user 'yuzu' with random UUID.");
318 return;
319 }
320
321 ProfileDataRaw data;
322 if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
323 LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
324 "'yuzu' with random UUID.");
325 return;
326 }
327
328 for (std::size_t i = 0; i < MAX_USERS; ++i) {
329 const auto& user = data.users[i];
330
331 if (user.uuid != UUID(INVALID_UUID))
332 AddUser({user.uuid, user.username, user.timestamp, {}, false});
333 }
334
335 std::stable_partition(profiles.begin(), profiles.end(),
336 [](const ProfileInfo& profile) { return profile.user_uuid; });
337}
338
339void ProfileManager::WriteUserSaveFile() {
340 ProfileDataRaw raw{};
341
342 for (std::size_t i = 0; i < MAX_USERS; ++i) {
343 raw.users[i].username = profiles[i].username;
344 raw.users[i].uuid2 = profiles[i].user_uuid;
345 raw.users[i].uuid = profiles[i].user_uuid;
346 raw.users[i].timestamp = profiles[i].creation_time;
347 }
348
349 const auto raw_path =
350 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
351 if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
352 FileUtil::Delete(raw_path);
353
354 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
355 ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
356
357 if (!FileUtil::CreateFullPath(path)) {
358 LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
359 "nand/system/save/8000000000000010/su/avators to mitigate this "
360 "issue.");
361 return;
362 }
363
364 FileUtil::IOFile save(path, "wb");
365
366 if (!save.IsOpen()) {
367 LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
368 "made in current session will be saved.");
369 return;
370 }
371
372 save.Resize(sizeof(ProfileDataRaw));
373 save.WriteBytes(&raw, sizeof(ProfileDataRaw));
374}
375
242}; // namespace Service::Account 376}; // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index bffd4cf4d..747c46c20 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -5,8 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8 9
9#include "boost/optional.hpp"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/swap.h" 11#include "common/swap.h"
12#include "core/hle/result.h" 12#include "core/hle/result.h"
@@ -36,7 +36,7 @@ struct UUID {
36 } 36 }
37 37
38 // TODO(ogniK): Properly generate uuids based on RFC-4122 38 // TODO(ogniK): Properly generate uuids based on RFC-4122
39 const UUID& Generate(); 39 static UUID Generate();
40 40
41 // Set the UUID to {0,0} to be considered an invalid user 41 // Set the UUID to {0,0} to be considered an invalid user
42 void Invalidate() { 42 void Invalidate() {
@@ -45,10 +45,20 @@ struct UUID {
45 std::string Format() const { 45 std::string Format() const {
46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); 46 return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
47 } 47 }
48
49 std::string FormatSwitch() const {
50 std::array<u8, 16> s{};
51 std::memcpy(s.data(), uuid.data(), sizeof(u128));
52 return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
53 ":02x}{:02x}{:02x}{:02x}{:02x}",
54 s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
55 s[12], s[13], s[14], s[15]);
56 }
48}; 57};
49static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); 58static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
50 59
51using ProfileUsername = std::array<u8, 0x20>; 60constexpr std::size_t profile_username_size = 32;
61using ProfileUsername = std::array<u8, profile_username_size>;
52using ProfileData = std::array<u8, MAX_DATA>; 62using ProfileData = std::array<u8, MAX_DATA>;
53using UserIDArray = std::array<UUID, MAX_USERS>; 63using UserIDArray = std::array<UUID, MAX_USERS>;
54 64
@@ -81,18 +91,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
81/// objects 91/// objects
82class ProfileManager { 92class ProfileManager {
83public: 93public:
84 ProfileManager(); // TODO(ogniK): Load from system save 94 ProfileManager();
85 ~ProfileManager(); 95 ~ProfileManager();
86 96
87 ResultCode AddUser(const ProfileInfo& user); 97 ResultCode AddUser(const ProfileInfo& user);
88 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); 98 ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
89 ResultCode CreateNewUser(UUID uuid, const std::string& username); 99 ResultCode CreateNewUser(UUID uuid, const std::string& username);
90 boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; 100 std::optional<UUID> GetUser(std::size_t index) const;
91 boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; 101 std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
92 bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; 102 std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
103 bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
93 bool GetProfileBase(UUID uuid, ProfileBase& profile) const; 104 bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
94 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; 105 bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
95 bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, 106 bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
96 ProfileData& data) const; 107 ProfileData& data) const;
97 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; 108 bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
98 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, 109 bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
@@ -100,6 +111,7 @@ public:
100 std::size_t GetUserCount() const; 111 std::size_t GetUserCount() const;
101 std::size_t GetOpenUserCount() const; 112 std::size_t GetOpenUserCount() const;
102 bool UserExists(UUID uuid) const; 113 bool UserExists(UUID uuid) const;
114 bool UserExistsIndex(std::size_t index) const;
103 void OpenUser(UUID uuid); 115 void OpenUser(UUID uuid);
104 void CloseUser(UUID uuid); 116 void CloseUser(UUID uuid);
105 UserIDArray GetOpenUsers() const; 117 UserIDArray GetOpenUsers() const;
@@ -108,11 +120,17 @@ public:
108 120
109 bool CanSystemRegisterUser() const; 121 bool CanSystemRegisterUser() const;
110 122
123 bool RemoveUser(UUID uuid);
124 bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
125
111private: 126private:
127 void ParseUserSaveFile();
128 void WriteUserSaveFile();
129 std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
130 bool RemoveProfileAtIndex(std::size_t index);
131
112 std::array<ProfileInfo, MAX_USERS> profiles{}; 132 std::array<ProfileInfo, MAX_USERS> profiles{};
113 std::size_t user_count = 0; 133 std::size_t user_count = 0;
114 boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
115 bool RemoveProfileAtIndex(std::size_t index);
116 UUID last_opened_user{INVALID_UUID}; 134 UUID last_opened_user{INVALID_UUID};
117}; 135};
118 136
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 69bfce1c1..ac3ff9f20 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -4,17 +4,20 @@
4 4
5#include <array> 5#include <array>
6#include <cinttypes> 6#include <cinttypes>
7#include <cstring>
7#include <stack> 8#include <stack>
8#include "core/core.h" 9#include "core/core.h"
9#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/event.h" 11#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
13#include "core/hle/service/acc/profile_manager.h"
12#include "core/hle/service/am/am.h" 14#include "core/hle/service/am/am.h"
13#include "core/hle/service/am/applet_ae.h" 15#include "core/hle/service/am/applet_ae.h"
14#include "core/hle/service/am/applet_oe.h" 16#include "core/hle/service/am/applet_oe.h"
15#include "core/hle/service/am/idle.h" 17#include "core/hle/service/am/idle.h"
16#include "core/hle/service/am/omm.h" 18#include "core/hle/service/am/omm.h"
17#include "core/hle/service/am/spsm.h" 19#include "core/hle/service/am/spsm.h"
20#include "core/hle/service/am/tcap.h"
18#include "core/hle/service/apm/apm.h" 21#include "core/hle/service/apm/apm.h"
19#include "core/hle/service/filesystem/filesystem.h" 22#include "core/hle/service/filesystem/filesystem.h"
20#include "core/hle/service/nvflinger/nvflinger.h" 23#include "core/hle/service/nvflinger/nvflinger.h"
@@ -25,14 +28,29 @@
25 28
26namespace Service::AM { 29namespace Service::AM {
27 30
31constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
32
33struct LaunchParameters {
34 u32_le magic;
35 u32_le is_account_selected;
36 u128 current_user;
37 INSERT_PADDING_BYTES(0x70);
38};
39static_assert(sizeof(LaunchParameters) == 0x88);
40
28IWindowController::IWindowController() : ServiceFramework("IWindowController") { 41IWindowController::IWindowController() : ServiceFramework("IWindowController") {
42 // clang-format off
29 static const FunctionInfo functions[] = { 43 static const FunctionInfo functions[] = {
30 {0, nullptr, "CreateWindow"}, 44 {0, nullptr, "CreateWindow"},
31 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, 45 {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
32 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, 46 {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
33 {11, nullptr, "ReleaseForegroundRights"}, 47 {11, nullptr, "ReleaseForegroundRights"},
34 {12, nullptr, "RejectToChangeIntoBackground"}, 48 {12, nullptr, "RejectToChangeIntoBackground"},
49 {20, nullptr, "SetAppletWindowVisibility"},
50 {21, nullptr, "SetAppletGpuTimeSlice"},
35 }; 51 };
52 // clang-format on
53
36 RegisterHandlers(functions); 54 RegisterHandlers(functions);
37} 55}
38 56
@@ -87,6 +105,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
87} 105}
88 106
89IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") { 107IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
108 // clang-format off
90 static const FunctionInfo functions[] = { 109 static const FunctionInfo functions[] = {
91 {0, nullptr, "GetLastForegroundCaptureImage"}, 110 {0, nullptr, "GetLastForegroundCaptureImage"},
92 {1, nullptr, "UpdateLastForegroundCaptureImage"}, 111 {1, nullptr, "UpdateLastForegroundCaptureImage"},
@@ -117,7 +136,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
117 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, 136 {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
118 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"}, 137 {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
119 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"}, 138 {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
139 // 6.0.0+
140 {28, nullptr, "TakeScreenShotOfOwnLayerEx"},
120 }; 141 };
142 // clang-format on
143
121 RegisterHandlers(functions); 144 RegisterHandlers(functions);
122} 145}
123 146
@@ -128,6 +151,7 @@ IDebugFunctions::~IDebugFunctions() = default;
128 151
129ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 152ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
130 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { 153 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
154 // clang-format off
131 static const FunctionInfo functions[] = { 155 static const FunctionInfo functions[] = {
132 {0, nullptr, "Exit"}, 156 {0, nullptr, "Exit"},
133 {1, &ISelfController::LockExit, "LockExit"}, 157 {1, &ISelfController::LockExit, "LockExit"},
@@ -136,10 +160,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
136 {4, nullptr, "LeaveFatalSection"}, 160 {4, nullptr, "LeaveFatalSection"},
137 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, 161 {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
138 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, 162 {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
139 {11, &ISelfController::SetOperationModeChangedNotification, 163 {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
140 "SetOperationModeChangedNotification"}, 164 {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
141 {12, &ISelfController::SetPerformanceModeChangedNotification,
142 "SetPerformanceModeChangedNotification"},
143 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, 165 {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
144 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, 166 {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
145 {15, nullptr, "SetScreenShotAppletIdentityInfo"}, 167 {15, nullptr, "SetScreenShotAppletIdentityInfo"},
@@ -165,7 +187,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
165 {69, nullptr, "IsAutoSleepDisabled"}, 187 {69, nullptr, "IsAutoSleepDisabled"},
166 {70, nullptr, "ReportMultimediaError"}, 188 {70, nullptr, "ReportMultimediaError"},
167 {80, nullptr, "SetWirelessPriorityMode"}, 189 {80, nullptr, "SetWirelessPriorityMode"},
190 {90, nullptr, "GetAccumulatedSuspendedTickValue"},
191 {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
192 {1000, nullptr, "GetDebugStorageChannel"},
168 }; 193 };
194 // clang-format on
195
169 RegisterHandlers(functions); 196 RegisterHandlers(functions);
170 197
171 auto& kernel = Core::System::GetInstance().Kernel(); 198 auto& kernel = Core::System::GetInstance().Kernel();
@@ -312,6 +339,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
312} 339}
313 340
314ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { 341ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
342 // clang-format off
315 static const FunctionInfo functions[] = { 343 static const FunctionInfo functions[] = {
316 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, 344 {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
317 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, 345 {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
@@ -336,11 +364,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
336 {52, nullptr, "SwitchLcdBacklight"}, 364 {52, nullptr, "SwitchLcdBacklight"},
337 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 365 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
338 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, 366 {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
339 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, 367 {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
340 "GetDefaultDisplayResolutionChangeEvent"},
341 {62, nullptr, "GetHdcpAuthenticationState"}, 368 {62, nullptr, "GetHdcpAuthenticationState"},
342 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, 369 {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
343 }; 370 };
371 // clang-format on
372
344 RegisterHandlers(functions); 373 RegisterHandlers(functions);
345 374
346 auto& kernel = Core::System::GetInstance().Kernel(); 375 auto& kernel = Core::System::GetInstance().Kernel();
@@ -432,11 +461,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
432public: 461public:
433 explicit IStorageAccessor(std::vector<u8> buffer) 462 explicit IStorageAccessor(std::vector<u8> buffer)
434 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { 463 : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
464 // clang-format off
435 static const FunctionInfo functions[] = { 465 static const FunctionInfo functions[] = {
436 {0, &IStorageAccessor::GetSize, "GetSize"}, 466 {0, &IStorageAccessor::GetSize, "GetSize"},
437 {10, &IStorageAccessor::Write, "Write"}, 467 {10, &IStorageAccessor::Write, "Write"},
438 {11, &IStorageAccessor::Read, "Read"}, 468 {11, &IStorageAccessor::Read, "Read"},
439 }; 469 };
470 // clang-format on
471
440 RegisterHandlers(functions); 472 RegisterHandlers(functions);
441 } 473 }
442 474
@@ -489,10 +521,13 @@ class IStorage final : public ServiceFramework<IStorage> {
489public: 521public:
490 explicit IStorage(std::vector<u8> buffer) 522 explicit IStorage(std::vector<u8> buffer)
491 : ServiceFramework("IStorage"), buffer(std::move(buffer)) { 523 : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
524 // clang-format off
492 static const FunctionInfo functions[] = { 525 static const FunctionInfo functions[] = {
493 {0, &IStorage::Open, "Open"}, 526 {0, &IStorage::Open, "Open"},
494 {1, nullptr, "OpenTransferStorage"}, 527 {1, nullptr, "OpenTransferStorage"},
495 }; 528 };
529 // clang-format on
530
496 RegisterHandlers(functions); 531 RegisterHandlers(functions);
497 } 532 }
498 533
@@ -512,6 +547,7 @@ private:
512class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { 547class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
513public: 548public:
514 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { 549 explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
550 // clang-format off
515 static const FunctionInfo functions[] = { 551 static const FunctionInfo functions[] = {
516 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, 552 {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
517 {1, nullptr, "IsCompleted"}, 553 {1, nullptr, "IsCompleted"},
@@ -532,6 +568,8 @@ public:
532 {150, nullptr, "RequestForAppletToGetForeground"}, 568 {150, nullptr, "RequestForAppletToGetForeground"},
533 {160, nullptr, "GetIndirectLayerConsumerHandle"}, 569 {160, nullptr, "GetIndirectLayerConsumerHandle"},
534 }; 570 };
571 // clang-format on
572
535 RegisterHandlers(functions); 573 RegisterHandlers(functions);
536 574
537 auto& kernel = Core::System::GetInstance().Kernel(); 575 auto& kernel = Core::System::GetInstance().Kernel();
@@ -624,13 +662,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
624} 662}
625 663
626IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { 664IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
665 // clang-format off
627 static const FunctionInfo functions[] = { 666 static const FunctionInfo functions[] = {
628 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, 667 {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
629 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, 668 {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
630 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, 669 {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
631 {12, nullptr, "CreateApplicationAndRequestToStart"}, 670 {12, nullptr, "CreateApplicationAndRequestToStart"},
632 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, 671 {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
633 "CreateApplicationAndRequestToStartForQuest"},
634 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, 672 {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
635 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, 673 {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
636 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, 674 {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -638,10 +676,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
638 {24, nullptr, "GetLaunchStorageInfoForDebug"}, 676 {24, nullptr, "GetLaunchStorageInfoForDebug"},
639 {25, nullptr, "ExtendSaveData"}, 677 {25, nullptr, "ExtendSaveData"},
640 {26, nullptr, "GetSaveDataSize"}, 678 {26, nullptr, "GetSaveDataSize"},
641 {30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"}, 679 {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
642 {31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"}, 680 {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
643 {32, nullptr, "BeginBlockingHomeButton"}, 681 {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
644 {33, nullptr, "EndBlockingHomeButton"}, 682 {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
645 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, 683 {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
646 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, 684 {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
647 {60, nullptr, "SetMediaPlaybackStateForApplication"}, 685 {60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -664,26 +702,57 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
664 {1000, nullptr, "CreateMovieMaker"}, 702 {1000, nullptr, "CreateMovieMaker"},
665 {1001, nullptr, "PrepareForJit"}, 703 {1001, nullptr, "PrepareForJit"},
666 }; 704 };
705 // clang-format on
706
667 RegisterHandlers(functions); 707 RegisterHandlers(functions);
668} 708}
669 709
670IApplicationFunctions::~IApplicationFunctions() = default; 710IApplicationFunctions::~IApplicationFunctions() = default;
671 711
712void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
713 Kernel::HLERequestContext& ctx) {
714 IPC::ResponseBuilder rb{ctx, 2};
715 rb.Push(RESULT_SUCCESS);
716 LOG_WARNING(Service_AM, "(STUBBED) called");
717}
718
719void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
720 Kernel::HLERequestContext& ctx) {
721 IPC::ResponseBuilder rb{ctx, 2};
722 rb.Push(RESULT_SUCCESS);
723 LOG_WARNING(Service_AM, "(STUBBED) called");
724}
725
726void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
727 IPC::ResponseBuilder rb{ctx, 2};
728 rb.Push(RESULT_SUCCESS);
729 LOG_WARNING(Service_AM, "(STUBBED) called");
730}
731
732void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
733 IPC::ResponseBuilder rb{ctx, 2};
734 rb.Push(RESULT_SUCCESS);
735 LOG_WARNING(Service_AM, "(STUBBED) called");
736}
737
672void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 738void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
673 constexpr std::array<u8, 0x88> data{{ 739 LaunchParameters params{};
674 0xca, 0x97, 0x94, 0xc7, // Magic
675 1, 0, 0, 0, // IsAccountSelected (bool)
676 1, 0, 0, 0, // User Id (word 0)
677 0, 0, 0, 0, // User Id (word 1)
678 0, 0, 0, 0, // User Id (word 2)
679 0, 0, 0, 0 // User Id (word 3)
680 }};
681 740
682 std::vector<u8> buffer(data.begin(), data.end()); 741 params.magic = POP_LAUNCH_PARAMETER_MAGIC;
742 params.is_account_selected = 1;
743
744 Account::ProfileManager profile_manager{};
745 const auto uuid = profile_manager.GetUser(Settings::values.current_user);
746 ASSERT(uuid);
747 params.current_user = uuid->uuid;
683 748
684 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 749 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
685 750
686 rb.Push(RESULT_SUCCESS); 751 rb.Push(RESULT_SUCCESS);
752
753 std::vector<u8> buffer(sizeof(LaunchParameters));
754 std::memcpy(buffer.data(), &params, buffer.size());
755
687 rb.PushIpcInterface<AM::IStorage>(buffer); 756 rb.PushIpcInterface<AM::IStorage>(buffer);
688 757
689 LOG_DEBUG(Service_AM, "called"); 758 LOG_DEBUG(Service_AM, "called");
@@ -776,9 +845,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
776 std::make_shared<IdleSys>()->InstallAsService(service_manager); 845 std::make_shared<IdleSys>()->InstallAsService(service_manager);
777 std::make_shared<OMM>()->InstallAsService(service_manager); 846 std::make_shared<OMM>()->InstallAsService(service_manager);
778 std::make_shared<SPSM>()->InstallAsService(service_manager); 847 std::make_shared<SPSM>()->InstallAsService(service_manager);
848 std::make_shared<TCAP>()->InstallAsService(service_manager);
779} 849}
780 850
781IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { 851IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
852 // clang-format off
782 static const FunctionInfo functions[] = { 853 static const FunctionInfo functions[] = {
783 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, 854 {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
784 {11, nullptr, "LockForeground"}, 855 {11, nullptr, "LockForeground"},
@@ -787,7 +858,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
787 {21, nullptr, "GetPopFromGeneralChannelEvent"}, 858 {21, nullptr, "GetPopFromGeneralChannelEvent"},
788 {30, nullptr, "GetHomeButtonWriterLockAccessor"}, 859 {30, nullptr, "GetHomeButtonWriterLockAccessor"},
789 {31, nullptr, "GetWriterLockAccessorEx"}, 860 {31, nullptr, "GetWriterLockAccessorEx"},
861 {100, nullptr, "PopRequestLaunchApplicationForDebug"},
790 }; 862 };
863 // clang-format on
864
791 RegisterHandlers(functions); 865 RegisterHandlers(functions);
792} 866}
793 867
@@ -800,6 +874,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
800} 874}
801 875
802IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") { 876IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
877 // clang-format off
803 static const FunctionInfo functions[] = { 878 static const FunctionInfo functions[] = {
804 {0, nullptr, "RequestToEnterSleep"}, 879 {0, nullptr, "RequestToEnterSleep"},
805 {1, nullptr, "EnterSleep"}, 880 {1, nullptr, "EnterSleep"},
@@ -813,18 +888,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
813 {14, nullptr, "ShouldSleepOnBoot"}, 888 {14, nullptr, "ShouldSleepOnBoot"},
814 {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, 889 {15, nullptr, "GetHdcpAuthenticationFailedEvent"},
815 }; 890 };
891 // clang-format on
892
816 RegisterHandlers(functions); 893 RegisterHandlers(functions);
817} 894}
818 895
819IGlobalStateController::~IGlobalStateController() = default; 896IGlobalStateController::~IGlobalStateController() = default;
820 897
821IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") { 898IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
899 // clang-format off
822 static const FunctionInfo functions[] = { 900 static const FunctionInfo functions[] = {
823 {0, nullptr, "CreateApplication"}, 901 {0, nullptr, "CreateApplication"},
824 {1, nullptr, "PopLaunchRequestedApplication"}, 902 {1, nullptr, "PopLaunchRequestedApplication"},
825 {10, nullptr, "CreateSystemApplication"}, 903 {10, nullptr, "CreateSystemApplication"},
826 {100, nullptr, "PopFloatingApplicationForDevelopment"}, 904 {100, nullptr, "PopFloatingApplicationForDevelopment"},
827 }; 905 };
906 // clang-format on
907
828 RegisterHandlers(functions); 908 RegisterHandlers(functions);
829} 909}
830 910
@@ -832,6 +912,7 @@ IApplicationCreator::~IApplicationCreator() = default;
832 912
833IProcessWindingController::IProcessWindingController() 913IProcessWindingController::IProcessWindingController()
834 : ServiceFramework("IProcessWindingController") { 914 : ServiceFramework("IProcessWindingController") {
915 // clang-format off
835 static const FunctionInfo functions[] = { 916 static const FunctionInfo functions[] = {
836 {0, nullptr, "GetLaunchReason"}, 917 {0, nullptr, "GetLaunchReason"},
837 {11, nullptr, "OpenCallingLibraryApplet"}, 918 {11, nullptr, "OpenCallingLibraryApplet"},
@@ -842,6 +923,8 @@ IProcessWindingController::IProcessWindingController()
842 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, 923 {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
843 {41, nullptr, "ReserveToStartAndWait"}, 924 {41, nullptr, "ReserveToStartAndWait"},
844 }; 925 };
926 // clang-format on
927
845 RegisterHandlers(functions); 928 RegisterHandlers(functions);
846} 929}
847 930
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index b39b0d838..095f94851 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -154,6 +154,10 @@ private:
154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); 154 void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
155 void NotifyRunning(Kernel::HLERequestContext& ctx); 155 void NotifyRunning(Kernel::HLERequestContext& ctx);
156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx); 156 void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
157 void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
158 void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
159 void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
160 void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
157}; 161};
158 162
159class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { 163class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 4296c255e..68ea778e8 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
211 211
212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 212AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
213 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { 213 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
214 // clang-format off
214 static const FunctionInfo functions[] = { 215 static const FunctionInfo functions[] = {
215 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 216 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
216 {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, 217 {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
@@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
218 {300, nullptr, "OpenOverlayAppletProxy"}, 219 {300, nullptr, "OpenOverlayAppletProxy"},
219 {350, nullptr, "OpenSystemApplicationProxy"}, 220 {350, nullptr, "OpenSystemApplicationProxy"},
220 {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, 221 {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
222 {401, nullptr, "GetSystemAppletControllerForDebug"},
221 }; 223 };
224 // clang-format on
225
222 RegisterHandlers(functions); 226 RegisterHandlers(functions);
223} 227}
224 228
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index e45cf6e20..60717afd9 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 14public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) 15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
16 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { 16 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
17 // clang-format off
17 static const FunctionInfo functions[] = { 18 static const FunctionInfo functions[] = {
18 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 19 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
19 {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, 20 {1, &IApplicationProxy::GetSelfController, "GetSelfController"},
@@ -25,6 +26,8 @@ public:
25 {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, 26 {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
26 {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, 27 {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
27 }; 28 };
29 // clang-format on
30
28 RegisterHandlers(functions); 31 RegisterHandlers(functions);
29 } 32 }
30 33
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp
index 0e3088bc8..f814fe2c0 100644
--- a/src/core/hle/service/am/idle.cpp
+++ b/src/core/hle/service/am/idle.cpp
@@ -12,9 +12,9 @@ IdleSys::IdleSys() : ServiceFramework{"idle:sys"} {
12 {0, nullptr, "GetAutoPowerDownEvent"}, 12 {0, nullptr, "GetAutoPowerDownEvent"},
13 {1, nullptr, "Unknown1"}, 13 {1, nullptr, "Unknown1"},
14 {2, nullptr, "Unknown2"}, 14 {2, nullptr, "Unknown2"},
15 {3, nullptr, "Unknown3"}, 15 {3, nullptr, "SetHandlingContext"},
16 {4, nullptr, "Unknown4"}, 16 {4, nullptr, "LoadAndApplySettings"},
17 {5, nullptr, "Unknown5"}, 17 {5, nullptr, "ReportUserIsActive"},
18 }; 18 };
19 // clang-format on 19 // clang-format on
20 20
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
index 1c37f849f..6ab3fb906 100644
--- a/src/core/hle/service/am/omm.cpp
+++ b/src/core/hle/service/am/omm.cpp
@@ -17,22 +17,24 @@ OMM::OMM() : ServiceFramework{"omm"} {
17 {5, nullptr, "GetCradleStatus"}, 17 {5, nullptr, "GetCradleStatus"},
18 {6, nullptr, "FadeInDisplay"}, 18 {6, nullptr, "FadeInDisplay"},
19 {7, nullptr, "FadeOutDisplay"}, 19 {7, nullptr, "FadeOutDisplay"},
20 {8, nullptr, "Unknown1"}, 20 {8, nullptr, "GetCradleFwVersion"},
21 {9, nullptr, "Unknown2"}, 21 {9, nullptr, "NotifyCecSettingsChanged"},
22 {10, nullptr, "Unknown3"}, 22 {10, nullptr, "SetOperationModePolicy"},
23 {11, nullptr, "Unknown4"}, 23 {11, nullptr, "GetDefaultDisplayResolution"},
24 {12, nullptr, "Unknown5"}, 24 {12, nullptr, "GetDefaultDisplayResolutionChangeEvent"},
25 {13, nullptr, "Unknown6"}, 25 {13, nullptr, "UpdateDefaultDisplayResolution"},
26 {14, nullptr, "Unknown7"}, 26 {14, nullptr, "ShouldSleepOnBoot"},
27 {15, nullptr, "Unknown8"}, 27 {15, nullptr, "NotifyHdcpApplicationExecutionStarted"},
28 {16, nullptr, "Unknown9"}, 28 {16, nullptr, "NotifyHdcpApplicationExecutionFinished"},
29 {17, nullptr, "Unknown10"}, 29 {17, nullptr, "NotifyHdcpApplicationDrawingStarted"},
30 {18, nullptr, "Unknown11"}, 30 {18, nullptr, "NotifyHdcpApplicationDrawingFinished"},
31 {19, nullptr, "Unknown12"}, 31 {19, nullptr, "GetHdcpAuthenticationFailedEvent"},
32 {20, nullptr, "Unknown13"}, 32 {20, nullptr, "GetHdcpAuthenticationFailedEmulationEnabled"},
33 {21, nullptr, "Unknown14"}, 33 {21, nullptr, "SetHdcpAuthenticationFailedEmulation"},
34 {22, nullptr, "Unknown15"}, 34 {22, nullptr, "GetHdcpStateChangeEvent"},
35 {23, nullptr, "Unknown16"}, 35 {23, nullptr, "GetHdcpState"},
36 {24, nullptr, "ShowCardUpdateProcessing"},
37 {25, nullptr, "SetApplicationCecSettingsAndNotifyChanged"},
36 }; 38 };
37 // clang-format on 39 // clang-format on
38 40
diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp
new file mode 100644
index 000000000..a75cbdda8
--- /dev/null
+++ b/src/core/hle/service/am/tcap.cpp
@@ -0,0 +1,23 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/am/tcap.h"
6
7namespace Service::AM {
8
9TCAP::TCAP() : ServiceFramework{"tcap"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetContinuousHighSkinTemperatureEvent"},
13 {1, nullptr, "SetOperationMode"},
14 {2, nullptr, "LoadAndApplySettings"},
15 };
16 // clang-format on
17
18 RegisterHandlers(functions);
19}
20
21TCAP::~TCAP() = default;
22
23} // namespace Service::AM
diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h
new file mode 100644
index 000000000..2021b55d1
--- /dev/null
+++ b/src/core/hle/service/am/tcap.h
@@ -0,0 +1,17 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service::AM {
10
11class TCAP final : public ServiceFramework<TCAP> {
12public:
13 explicit TCAP();
14 ~TCAP() override;
15};
16
17} // namespace Service::AM
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 79580bcd9..54305cf05 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,10 +7,13 @@
7#include <vector> 7#include <vector>
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
10#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/partition_filesystem.h" 12#include "core/file_sys/partition_filesystem.h"
13#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
13#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
16#include "core/hle/kernel/event.h"
14#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
15#include "core/hle/service/aoc/aoc_u.h" 18#include "core/hle/service/aoc/aoc_u.h"
16#include "core/hle/service/filesystem/filesystem.h" 19#include "core/hle/service/filesystem/filesystem.h"
@@ -19,10 +22,10 @@
19namespace Service::AOC { 22namespace Service::AOC {
20 23
21constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 24constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
22constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; 25constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
23 26
24static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 27static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
25 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 28 return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
26} 29}
27 30
28static std::vector<u64> AccumulateAOCTitleIDs() { 31static std::vector<u64> AccumulateAOCTitleIDs() {
@@ -53,21 +56,25 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
53 {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"}, 56 {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},
54 {6, nullptr, "PrepareAddOnContentByApplicationId"}, 57 {6, nullptr, "PrepareAddOnContentByApplicationId"},
55 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, 58 {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},
56 {8, nullptr, "GetAddOnContentListChangedEvent"}, 59 {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"},
57 }; 60 };
58 RegisterHandlers(functions); 61 RegisterHandlers(functions);
62
63 auto& kernel = Core::System::GetInstance().Kernel();
64 aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
65 "GetAddOnContentListChanged:Event");
59} 66}
60 67
61AOC_U::~AOC_U() = default; 68AOC_U::~AOC_U() = default;
62 69
63void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { 70void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
64 IPC::ResponseBuilder rb{ctx, 4}; 71 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(RESULT_SUCCESS); 72 rb.Push(RESULT_SUCCESS);
66 73
67 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 74 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
68 rb.Push<u32>(std::count_if(add_on_content.begin(), add_on_content.end(), [&current](u64 tid) { 75 rb.Push<u32>(static_cast<u32>(
69 return (tid & DLC_BASE_TITLE_ID_MASK) == current; 76 std::count_if(add_on_content.begin(), add_on_content.end(),
70 })); 77 [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
71} 78}
72 79
73void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { 80void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
@@ -91,20 +98,30 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
91 return; 98 return;
92 } 99 }
93 100
94 count = std::min<size_t>(out.size() - offset, count); 101 count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
95 std::rotate(out.begin(), out.begin() + offset, out.end()); 102 std::rotate(out.begin(), out.begin() + offset, out.end());
96 out.resize(count); 103 out.resize(count);
97 104
98 ctx.WriteBuffer(out); 105 ctx.WriteBuffer(out);
99 106
100 IPC::ResponseBuilder rb{ctx, 2}; 107 IPC::ResponseBuilder rb{ctx, 3};
101 rb.Push(RESULT_SUCCESS); 108 rb.Push(RESULT_SUCCESS);
109 rb.Push(count);
102} 110}
103 111
104void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { 112void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
105 IPC::ResponseBuilder rb{ctx, 4}; 113 IPC::ResponseBuilder rb{ctx, 4};
106 rb.Push(RESULT_SUCCESS); 114 rb.Push(RESULT_SUCCESS);
107 rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); 115 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
116 FileSys::PatchManager pm{title_id};
117
118 const auto res = pm.GetControlMetadata();
119 if (res.first == nullptr) {
120 rb.Push(title_id + DLC_BASE_TO_AOC_ID);
121 return;
122 }
123
124 rb.Push(res.first->GetDLCBaseTitleId());
108} 125}
109 126
110void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { 127void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
@@ -118,6 +135,14 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
118 rb.Push(RESULT_SUCCESS); 135 rb.Push(RESULT_SUCCESS);
119} 136}
120 137
138void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
139 LOG_WARNING(Service_AOC, "(STUBBED) called");
140
141 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(aoc_change_event);
144}
145
121void InstallInterfaces(SM::ServiceManager& service_manager) { 146void InstallInterfaces(SM::ServiceManager& service_manager) {
122 std::make_shared<AOC_U>()->InstallAsService(service_manager); 147 std::make_shared<AOC_U>()->InstallAsService(service_manager);
123} 148}
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index b3c7cab7a..68d94fdaa 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -18,8 +18,10 @@ private:
18 void ListAddOnContent(Kernel::HLERequestContext& ctx); 18 void ListAddOnContent(Kernel::HLERequestContext& ctx);
19 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); 19 void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx);
20 void PrepareAddOnContent(Kernel::HLERequestContext& ctx); 20 void PrepareAddOnContent(Kernel::HLERequestContext& ctx);
21 void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
21 22
22 std::vector<u64> add_on_content; 23 std::vector<u64> add_on_content;
24 Kernel::SharedPtr<Kernel::Event> aoc_change_event;
23}; 25};
24 26
25/// Registers all AOC services with the specified service manager. 27/// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6073f4ecd..fac6785a5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -22,20 +22,22 @@ class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
22public: 22public:
23 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) 23 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
24 : ServiceFramework("IAudioRenderer") { 24 : ServiceFramework("IAudioRenderer") {
25 // clang-format off
25 static const FunctionInfo functions[] = { 26 static const FunctionInfo functions[] = {
26 {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, 27 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
27 {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, 28 {1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
28 {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, 29 {2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
29 {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"}, 30 {3, &IAudioRenderer::GetState, "GetState"},
30 {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, 31 {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
31 {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, 32 {5, &IAudioRenderer::Start, "Start"},
32 {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, 33 {6, &IAudioRenderer::Stop, "Stop"},
33 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, 34 {7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
34 {8, nullptr, "SetAudioRendererRenderingTimeLimit"}, 35 {8, nullptr, "SetRenderingTimeLimit"},
35 {9, nullptr, "GetAudioRendererRenderingTimeLimit"}, 36 {9, nullptr, "GetRenderingTimeLimit"},
36 {10, nullptr, "RequestUpdateAudioRendererAuto"}, 37 {10, nullptr, "RequestUpdateAuto"},
37 {11, nullptr, "ExecuteAudioRendererRendering"}, 38 {11, nullptr, "ExecuteAudioRendererRendering"},
38 }; 39 };
40 // clang-format on
39 RegisterHandlers(functions); 41 RegisterHandlers(functions);
40 42
41 auto& kernel = Core::System::GetInstance().Kernel(); 43 auto& kernel = Core::System::GetInstance().Kernel();
@@ -49,42 +51,42 @@ private:
49 system_event->Signal(); 51 system_event->Signal();
50 } 52 }
51 53
52 void GetAudioRendererSampleRate(Kernel::HLERequestContext& ctx) { 54 void GetSampleRate(Kernel::HLERequestContext& ctx) {
53 IPC::ResponseBuilder rb{ctx, 3}; 55 IPC::ResponseBuilder rb{ctx, 3};
54 rb.Push(RESULT_SUCCESS); 56 rb.Push(RESULT_SUCCESS);
55 rb.Push<u32>(renderer->GetSampleRate()); 57 rb.Push<u32>(renderer->GetSampleRate());
56 LOG_DEBUG(Service_Audio, "called"); 58 LOG_DEBUG(Service_Audio, "called");
57 } 59 }
58 60
59 void GetAudioRendererSampleCount(Kernel::HLERequestContext& ctx) { 61 void GetSampleCount(Kernel::HLERequestContext& ctx) {
60 IPC::ResponseBuilder rb{ctx, 3}; 62 IPC::ResponseBuilder rb{ctx, 3};
61 rb.Push(RESULT_SUCCESS); 63 rb.Push(RESULT_SUCCESS);
62 rb.Push<u32>(renderer->GetSampleCount()); 64 rb.Push<u32>(renderer->GetSampleCount());
63 LOG_DEBUG(Service_Audio, "called"); 65 LOG_DEBUG(Service_Audio, "called");
64 } 66 }
65 67
66 void GetAudioRendererState(Kernel::HLERequestContext& ctx) { 68 void GetState(Kernel::HLERequestContext& ctx) {
67 IPC::ResponseBuilder rb{ctx, 3}; 69 IPC::ResponseBuilder rb{ctx, 3};
68 rb.Push(RESULT_SUCCESS); 70 rb.Push(RESULT_SUCCESS);
69 rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); 71 rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
70 LOG_DEBUG(Service_Audio, "called"); 72 LOG_DEBUG(Service_Audio, "called");
71 } 73 }
72 74
73 void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { 75 void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
74 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx, 3};
75 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
76 rb.Push<u32>(renderer->GetMixBufferCount()); 78 rb.Push<u32>(renderer->GetMixBufferCount());
77 LOG_DEBUG(Service_Audio, "called"); 79 LOG_DEBUG(Service_Audio, "called");
78 } 80 }
79 81
80 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { 82 void RequestUpdate(Kernel::HLERequestContext& ctx) {
81 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); 83 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
82 IPC::ResponseBuilder rb{ctx, 2}; 84 IPC::ResponseBuilder rb{ctx, 2};
83 rb.Push(RESULT_SUCCESS); 85 rb.Push(RESULT_SUCCESS);
84 LOG_WARNING(Service_Audio, "(STUBBED) called"); 86 LOG_WARNING(Service_Audio, "(STUBBED) called");
85 } 87 }
86 88
87 void StartAudioRenderer(Kernel::HLERequestContext& ctx) { 89 void Start(Kernel::HLERequestContext& ctx) {
88 IPC::ResponseBuilder rb{ctx, 2}; 90 IPC::ResponseBuilder rb{ctx, 2};
89 91
90 rb.Push(RESULT_SUCCESS); 92 rb.Push(RESULT_SUCCESS);
@@ -92,7 +94,7 @@ private:
92 LOG_WARNING(Service_Audio, "(STUBBED) called"); 94 LOG_WARNING(Service_Audio, "(STUBBED) called");
93 } 95 }
94 96
95 void StopAudioRenderer(Kernel::HLERequestContext& ctx) { 97 void Stop(Kernel::HLERequestContext& ctx) {
96 IPC::ResponseBuilder rb{ctx, 2}; 98 IPC::ResponseBuilder rb{ctx, 2};
97 99
98 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
@@ -129,6 +131,7 @@ public:
129 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, 131 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
130 {11, nullptr, "QueryAudioDeviceInputEvent"}, 132 {11, nullptr, "QueryAudioDeviceInputEvent"},
131 {12, nullptr, "QueryAudioDeviceOutputEvent"}, 133 {12, nullptr, "QueryAudioDeviceOutputEvent"},
134 {13, nullptr, "GetAudioSystemMasterVolumeSetting"},
132 }; 135 };
133 RegisterHandlers(functions); 136 RegisterHandlers(functions);
134 137
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index fc6067e59..783c39503 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -2,8 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
5#include <cstring> 6#include <cstring>
6#include <memory> 7#include <memory>
8#include <optional>
7#include <vector> 9#include <vector>
8 10
9#include <opus.h> 11#include <opus.h>
@@ -33,7 +35,8 @@ public:
33 {1, nullptr, "SetContext"}, 35 {1, nullptr, "SetContext"},
34 {2, nullptr, "DecodeInterleavedForMultiStream"}, 36 {2, nullptr, "DecodeInterleavedForMultiStream"},
35 {3, nullptr, "SetContextForMultiStream"}, 37 {3, nullptr, "SetContextForMultiStream"},
36 {4, nullptr, "Unknown4"}, 38 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerformance,
39 "DecodeInterleavedWithPerformance"},
37 {5, nullptr, "Unknown5"}, 40 {5, nullptr, "Unknown5"},
38 {6, nullptr, "Unknown6"}, 41 {6, nullptr, "Unknown6"},
39 {7, nullptr, "Unknown7"}, 42 {7, nullptr, "Unknown7"},
@@ -59,8 +62,31 @@ private:
59 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 62 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
60 } 63 }
61 64
62 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, 65 void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
63 std::vector<opus_int16>& output) { 66 u32 consumed = 0;
67 u32 sample_count = 0;
68 u64 performance = 0;
69 std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
70 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
71 performance)) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 // TODO(ogniK): Use correct error code
74 rb.Push(ResultCode(-1));
75 return;
76 }
77 IPC::ResponseBuilder rb{ctx, 6};
78 rb.Push(RESULT_SUCCESS);
79 rb.Push<u32>(consumed);
80 rb.Push<u64>(performance);
81 rb.Push<u32>(sample_count);
82 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
83 }
84
85 bool Decoder_DecodeInterleaved(
86 u32& consumed, u32& sample_count, const std::vector<u8>& input,
87 std::vector<opus_int16>& output,
88 std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
89 const auto start_time = std::chrono::high_resolution_clock::now();
64 std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 90 std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
65 if (sizeof(OpusHeader) > input.size()) 91 if (sizeof(OpusHeader) > input.size())
66 return false; 92 return false;
@@ -80,8 +106,13 @@ private:
80 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); 106 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
81 if (out_sample_count < 0) 107 if (out_sample_count < 0)
82 return false; 108 return false;
109 const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
83 sample_count = out_sample_count; 110 sample_count = out_sample_count;
84 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); 111 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
112 if (performance_time.has_value()) {
113 performance_time->get() =
114 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
115 }
85 return true; 116 return true;
86 } 117 }
87 118
@@ -130,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
130 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 161 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
131 162
132 std::size_t worker_sz = WorkerBufferSize(channel_count); 163 std::size_t worker_sz = WorkerBufferSize(channel_count);
133 ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); 164 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
134 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ 165 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
135 static_cast<OpusDecoder*>(operator new(worker_sz))}; 166 static_cast<OpusDecoder*>(operator new(worker_sz))};
136 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 167 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index d40f18565..6701cb913 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -9,6 +9,7 @@ namespace Service::ES {
9class ETicket final : public ServiceFramework<ETicket> { 9class ETicket final : public ServiceFramework<ETicket> {
10public: 10public:
11 explicit ETicket() : ServiceFramework{"es"} { 11 explicit ETicket() : ServiceFramework{"es"} {
12 // clang-format off
12 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
13 {1, nullptr, "ImportTicket"}, 14 {1, nullptr, "ImportTicket"},
14 {2, nullptr, "ImportTicketCertificateSet"}, 15 {2, nullptr, "ImportTicketCertificateSet"},
@@ -37,15 +38,18 @@ public:
37 {25, nullptr, "DeletePrepurchaseRecord"}, 38 {25, nullptr, "DeletePrepurchaseRecord"},
38 {26, nullptr, "DeleteAllPrepurchaseRecord"}, 39 {26, nullptr, "DeleteAllPrepurchaseRecord"},
39 {27, nullptr, "CountPrepurchaseRecord"}, 40 {27, nullptr, "CountPrepurchaseRecord"},
40 {28, nullptr, "ListPrepurchaseRecord"}, 41 {28, nullptr, "ListPrepurchaseRecordRightsIds"},
41 {29, nullptr, "ListPrepurchaseRecordInfo"}, 42 {29, nullptr, "ListPrepurchaseRecordInfo"},
42 {30, nullptr, "Unknown1"}, 43 {30, nullptr, "CountTicket"},
43 {31, nullptr, "Unknown2"}, 44 {31, nullptr, "ListTicketRightsIds"},
44 {32, nullptr, "Unknown3"}, 45 {32, nullptr, "CountPrepurchaseRecordEx"},
45 {33, nullptr, "Unknown4"}, 46 {33, nullptr, "ListPrepurchaseRecordRightsIdsEx"},
46 {34, nullptr, "Unknown5"}, 47 {34, nullptr, "GetEncryptedTicketSize"},
47 {35, nullptr, "Unknown6"}, 48 {35, nullptr, "GetEncryptedTicketData"},
49 {36, nullptr, "DeleteAllInactiveELicenseRequiredPersonalizedTicket"},
50 {503, nullptr, "GetTitleKey"},
48 }; 51 };
52 // clang-format on
49 RegisterHandlers(functions); 53 RegisterHandlers(functions);
50 } 54 }
51}; 55};
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index aed2abb71..e32a7c48e 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -264,6 +264,15 @@ ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
264 return RESULT_SUCCESS; 264 return RESULT_SUCCESS;
265} 265}
266 266
267void SetPackedUpdate(FileSys::VirtualFile update_raw) {
268 LOG_TRACE(Service_FS, "Setting packed update for romfs");
269
270 if (romfs_factory == nullptr)
271 return;
272
273 romfs_factory->SetPackedUpdate(std::move(update_raw));
274}
275
267ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { 276ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
268 LOG_TRACE(Service_FS, "Opening RomFS for current process"); 277 LOG_TRACE(Service_FS, "Opening RomFS for current process");
269 278
@@ -310,13 +319,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
310 return sdmc_factory->Open(); 319 return sdmc_factory->Open();
311} 320}
312 321
313std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { 322std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
314 return std::make_shared<FileSys::RegisteredCacheUnion>( 323 return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
315 std::vector<std::shared_ptr<FileSys::RegisteredCache>>{ 324 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
316 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
317} 325}
318 326
319std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { 327FileSys::RegisteredCache* GetSystemNANDContents() {
320 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 328 LOG_TRACE(Service_FS, "Opening System NAND Contents");
321 329
322 if (bis_factory == nullptr) 330 if (bis_factory == nullptr)
@@ -325,7 +333,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
325 return bis_factory->GetSystemNANDContents(); 333 return bis_factory->GetSystemNANDContents();
326} 334}
327 335
328std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() { 336FileSys::RegisteredCache* GetUserNANDContents() {
329 LOG_TRACE(Service_FS, "Opening User NAND Contents"); 337 LOG_TRACE(Service_FS, "Opening User NAND Contents");
330 338
331 if (bis_factory == nullptr) 339 if (bis_factory == nullptr)
@@ -334,7 +342,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {
334 return bis_factory->GetUserNANDContents(); 342 return bis_factory->GetUserNANDContents();
335} 343}
336 344
337std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() { 345FileSys::RegisteredCache* GetSDMCContents() {
338 LOG_TRACE(Service_FS, "Opening SDMC Contents"); 346 LOG_TRACE(Service_FS, "Opening SDMC Contents");
339 347
340 if (sdmc_factory == nullptr) 348 if (sdmc_factory == nullptr)
@@ -352,19 +360,19 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
352 return bis_factory->GetModificationLoadRoot(title_id); 360 return bis_factory->GetModificationLoadRoot(title_id);
353} 361}
354 362
355void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { 363void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
356 if (overwrite) { 364 if (overwrite) {
357 bis_factory = nullptr; 365 bis_factory = nullptr;
358 save_data_factory = nullptr; 366 save_data_factory = nullptr;
359 sdmc_factory = nullptr; 367 sdmc_factory = nullptr;
360 } 368 }
361 369
362 auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 370 auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
363 FileSys::Mode::ReadWrite); 371 FileSys::Mode::ReadWrite);
364 auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 372 auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
365 FileSys::Mode::ReadWrite); 373 FileSys::Mode::ReadWrite);
366 auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), 374 auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
367 FileSys::Mode::ReadWrite); 375 FileSys::Mode::ReadWrite);
368 376
369 if (bis_factory == nullptr) 377 if (bis_factory == nullptr)
370 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); 378 bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
@@ -374,7 +382,7 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
374 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); 382 sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
375} 383}
376 384
377void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { 385void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
378 romfs_factory = nullptr; 386 romfs_factory = nullptr;
379 CreateFactories(vfs, false); 387 CreateFactories(vfs, false);
380 std::make_shared<FSP_LDR>()->InstallAsService(service_manager); 388 std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 7039a2247..6ca5c5636 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -39,6 +39,7 @@ ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory)
39ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); 39ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
40ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); 40ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
41 41
42void SetPackedUpdate(FileSys::VirtualFile update_raw);
42ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); 43ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
43ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, 44ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
44 FileSys::ContentRecordType type); 45 FileSys::ContentRecordType type);
@@ -46,19 +47,19 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
46 FileSys::SaveDataDescriptor save_struct); 47 FileSys::SaveDataDescriptor save_struct);
47ResultVal<FileSys::VirtualDir> OpenSDMC(); 48ResultVal<FileSys::VirtualDir> OpenSDMC();
48 49
49std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 50std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
50 51
51std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); 52FileSys::RegisteredCache* GetSystemNANDContents();
52std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); 53FileSys::RegisteredCache* GetUserNANDContents();
53std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); 54FileSys::RegisteredCache* GetSDMCContents();
54 55
55FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
56 57
57// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 58// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
58// above is called. 59// above is called.
59void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); 60void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
60 61
61void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs); 62void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs);
62 63
63// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of 64// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
64// pointers and booleans. This makes using a VfsDirectory with switch services much easier and 65// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d5dced429..c1c83a11d 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -17,6 +17,7 @@
17#include "core/file_sys/errors.h" 17#include "core/file_sys/errors.h"
18#include "core/file_sys/mode.h" 18#include "core/file_sys/mode.h"
19#include "core/file_sys/nca_metadata.h" 19#include "core/file_sys/nca_metadata.h"
20#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/savedata_factory.h" 21#include "core/file_sys/savedata_factory.h"
21#include "core/file_sys/vfs.h" 22#include "core/file_sys/vfs.h"
22#include "core/hle/ipc_helpers.h" 23#include "core/hle/ipc_helpers.h"
@@ -272,8 +273,8 @@ public:
272 {0, &IFileSystem::CreateFile, "CreateFile"}, 273 {0, &IFileSystem::CreateFile, "CreateFile"},
273 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 274 {1, &IFileSystem::DeleteFile, "DeleteFile"},
274 {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, 275 {2, &IFileSystem::CreateDirectory, "CreateDirectory"},
275 {3, nullptr, "DeleteDirectory"}, 276 {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
276 {4, nullptr, "DeleteDirectoryRecursively"}, 277 {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
277 {5, &IFileSystem::RenameFile, "RenameFile"}, 278 {5, &IFileSystem::RenameFile, "RenameFile"},
278 {6, nullptr, "RenameDirectory"}, 279 {6, nullptr, "RenameDirectory"},
279 {7, &IFileSystem::GetEntryType, "GetEntryType"}, 280 {7, &IFileSystem::GetEntryType, "GetEntryType"},
@@ -328,6 +329,30 @@ public:
328 rb.Push(backend.CreateDirectory(name)); 329 rb.Push(backend.CreateDirectory(name));
329 } 330 }
330 331
332 void DeleteDirectory(Kernel::HLERequestContext& ctx) {
333 const IPC::RequestParser rp{ctx};
334
335 const auto file_buffer = ctx.ReadBuffer();
336 std::string name = Common::StringFromBuffer(file_buffer);
337
338 LOG_DEBUG(Service_FS, "called directory {}", name);
339
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(backend.DeleteDirectory(name));
342 }
343
344 void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
345 const IPC::RequestParser rp{ctx};
346
347 const auto file_buffer = ctx.ReadBuffer();
348 std::string name = Common::StringFromBuffer(file_buffer);
349
350 LOG_DEBUG(Service_FS, "called directory {}", name);
351
352 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(backend.DeleteDirectoryRecursively(name));
354 }
355
331 void RenameFile(Kernel::HLERequestContext& ctx) { 356 void RenameFile(Kernel::HLERequestContext& ctx) {
332 IPC::RequestParser rp{ctx}; 357 IPC::RequestParser rp{ctx};
333 358
@@ -630,6 +655,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
630 static_cast<u8>(storage_id), unknown, title_id); 655 static_cast<u8>(storage_id), unknown, title_id);
631 656
632 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 657 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
658
633 if (data.Failed()) { 659 if (data.Failed()) {
634 // TODO(DarkLordZach): Find the right error code to use here 660 // TODO(DarkLordZach): Find the right error code to use here
635 LOG_ERROR(Service_FS, 661 LOG_ERROR(Service_FS,
@@ -640,7 +666,9 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
640 return; 666 return;
641 } 667 }
642 668
643 IStorage storage(std::move(data.Unwrap())); 669 FileSys::PatchManager pm{title_id};
670
671 IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
644 672
645 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 673 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
646 rb.Push(RESULT_SUCCESS); 674 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
new file mode 100644
index 000000000..0993a7815
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -0,0 +1,30 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/hid/controllers/controller_base.h"
6
7namespace Service::HID {
8
9ControllerBase::ControllerBase() = default;
10ControllerBase::~ControllerBase() = default;
11
12void ControllerBase::ActivateController() {
13 if (is_activated) {
14 OnRelease();
15 }
16 is_activated = true;
17 OnInit();
18}
19
20void ControllerBase::DeactivateController() {
21 if (is_activated) {
22 OnRelease();
23 }
24 is_activated = false;
25}
26
27bool ControllerBase::IsControllerActivated() const {
28 return is_activated;
29}
30} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
new file mode 100644
index 000000000..f0e092b1b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/swap.h"
9
10namespace Service::HID {
11class ControllerBase {
12public:
13 ControllerBase();
14 virtual ~ControllerBase();
15
16 // Called when the controller is initialized
17 virtual void OnInit() = 0;
18
19 // When the controller is released
20 virtual void OnRelease() = 0;
21
22 // When the controller is requesting an update for the shared memory
23 virtual void OnUpdate(u8* data, std::size_t size) = 0;
24
25 // Called when input devices should be loaded
26 virtual void OnLoadInputDevices() = 0;
27
28 void ActivateController();
29
30 void DeactivateController();
31
32 bool IsControllerActivated() const;
33
34protected:
35 bool is_activated{false};
36
37 struct CommonHeader {
38 s64_le timestamp;
39 s64_le total_entry_count;
40 s64_le last_entry_index;
41 s64_le entry_count;
42 };
43 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
44};
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
new file mode 100644
index 000000000..3d100763f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -0,0 +1,42 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/debug_pad.h"
9
10namespace Service::HID {
11
12Controller_DebugPad::Controller_DebugPad() = default;
13Controller_DebugPad::~Controller_DebugPad() = default;
14
15void Controller_DebugPad::OnInit() {}
16
17void Controller_DebugPad::OnRelease() {}
18
19void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
20 shared_memory.header.timestamp = CoreTiming::GetTicks();
21 shared_memory.header.total_entry_count = 17;
22
23 if (!IsControllerActivated()) {
24 shared_memory.header.entry_count = 0;
25 shared_memory.header.last_entry_index = 0;
26 return;
27 }
28 shared_memory.header.entry_count = 16;
29
30 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
31 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
32 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
33
34 cur_entry.sampling_number = last_entry.sampling_number + 1;
35 cur_entry.sampling_number2 = cur_entry.sampling_number;
36 // TODO(ogniK): Update debug pad states
37
38 std::memcpy(data, &shared_memory, sizeof(SharedMemory));
39}
40
41void Controller_DebugPad::OnLoadInputDevices() {}
42} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
new file mode 100644
index 000000000..62b4f2682
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -0,0 +1,56 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_DebugPad final : public ControllerBase {
15public:
16 Controller_DebugPad();
17 ~Controller_DebugPad() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct AnalogStick {
33 s32_le x;
34 s32_le y;
35 };
36 static_assert(sizeof(AnalogStick) == 0x8);
37
38 struct PadStates {
39 s64_le sampling_number;
40 s64_le sampling_number2;
41 u32_le attribute;
42 u32_le button_state;
43 AnalogStick r_stick;
44 AnalogStick l_stick;
45 };
46 static_assert(sizeof(PadStates) == 0x28, "PadStates is an invalid state");
47
48 struct SharedMemory {
49 CommonHeader header;
50 std::array<PadStates, 17> pad_states;
51 INSERT_PADDING_BYTES(0x138);
52 };
53 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
54 SharedMemory shared_memory{};
55};
56} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
new file mode 100644
index 000000000..898572277
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/gesture.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
12
13Controller_Gesture::Controller_Gesture() = default;
14Controller_Gesture::~Controller_Gesture() = default;
15
16void Controller_Gesture::OnInit() {}
17
18void Controller_Gesture::OnRelease() {}
19
20void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.gesture_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update gesture states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Gesture::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
new file mode 100644
index 000000000..1056ffbcd
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -0,0 +1,63 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Gesture final : public ControllerBase {
14public:
15 Controller_Gesture();
16 ~Controller_Gesture() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct Locations {
32 s32_le x;
33 s32_le y;
34 };
35
36 struct GestureState {
37 s64_le sampling_number;
38 s64_le sampling_number2;
39
40 s64_le detection_count;
41 s32_le type;
42 s32_le dir;
43 s32_le x;
44 s32_le y;
45 s32_le delta_x;
46 s32_le delta_y;
47 f32 vel_x;
48 f32 vel_y;
49 s32_le attributes;
50 f32 scale;
51 f32 rotation;
52 s32_le location_count;
53 std::array<Locations, 4> locations;
54 };
55 static_assert(sizeof(GestureState) == 0x68, "GestureState is an invalid size");
56
57 struct SharedMemory {
58 CommonHeader header;
59 std::array<GestureState, 17> gesture_states;
60 };
61 SharedMemory shared_memory{};
62};
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
new file mode 100644
index 000000000..ccfbce9ac
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/keyboard.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
12
13Controller_Keyboard::Controller_Keyboard() = default;
14Controller_Keyboard::~Controller_Keyboard() = default;
15
16void Controller_Keyboard::OnInit() {}
17
18void Controller_Keyboard::OnRelease() {}
19
20void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 const auto& last_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.pad_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update keyboard states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Keyboard::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
new file mode 100644
index 000000000..493e68fce
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Keyboard final : public ControllerBase {
15public:
16 Controller_Keyboard();
17 ~Controller_Keyboard() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct KeyboardState {
33 s64_le sampling_number;
34 s64_le sampling_number2;
35
36 s32_le modifier;
37 s32_le attribute;
38 std::array<u8, 32> key;
39 };
40 static_assert(sizeof(KeyboardState) == 0x38, "KeyboardState is an invalid size");
41
42 struct SharedMemory {
43 CommonHeader header;
44 std::array<KeyboardState, 17> pad_states;
45 INSERT_PADDING_BYTES(0x28);
46 };
47 static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
48 SharedMemory shared_memory{};
49};
50} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
new file mode 100644
index 000000000..4e246a57d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -0,0 +1,43 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/mouse.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
12
13Controller_Mouse::Controller_Mouse() = default;
14Controller_Mouse::~Controller_Mouse() = default;
15
16void Controller_Mouse::OnInit() {}
17
18void Controller_Mouse::OnRelease() {}
19
20void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
21 shared_memory.header.timestamp = CoreTiming::GetTicks();
22 shared_memory.header.total_entry_count = 17;
23
24 if (!IsControllerActivated()) {
25 shared_memory.header.entry_count = 0;
26 shared_memory.header.last_entry_index = 0;
27 return;
28 }
29 shared_memory.header.entry_count = 16;
30
31 auto& last_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
32 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
33 auto& cur_entry = shared_memory.mouse_states[shared_memory.header.last_entry_index];
34
35 cur_entry.sampling_number = last_entry.sampling_number + 1;
36 cur_entry.sampling_number2 = cur_entry.sampling_number;
37 // TODO(ogniK): Update mouse states
38
39 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
40}
41
42void Controller_Mouse::OnLoadInputDevices() {}
43} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
new file mode 100644
index 000000000..543b0b71f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -0,0 +1,50 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_Mouse final : public ControllerBase {
14public:
15 Controller_Mouse();
16 ~Controller_Mouse() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct MouseState {
32 s64_le sampling_number;
33 s64_le sampling_number2;
34 s32_le x;
35 s32_le y;
36 s32_le delta_x;
37 s32_le delta_y;
38 s32_le mouse_wheel;
39 s32_le button;
40 s32_le attribute;
41 };
42 static_assert(sizeof(MouseState) == 0x30, "MouseState is an invalid size");
43
44 struct SharedMemory {
45 CommonHeader header;
46 std::array<MouseState, 17> mouse_states;
47 };
48 SharedMemory shared_memory{};
49};
50} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
new file mode 100644
index 000000000..4b4d1324f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -0,0 +1,620 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <cstring>
8#include "common/assert.h"
9#include "common/bit_field.h"
10#include "common/common_types.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/core_timing.h"
14#include "core/frontend/input.h"
15#include "core/hle/kernel/event.h"
16#include "core/hle/service/hid/controllers/npad.h"
17#include "core/settings.h"
18
19namespace Service::HID {
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
26constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
27constexpr std::size_t NPAD_OFFSET = 0x9A00;
28constexpr u32 BATTERY_FULL = 2;
29constexpr u32 NPAD_HANDHELD = 32;
30constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
31constexpr u32 MAX_NPAD_ID = 7;
32constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
33 Controller_NPad::NPadControllerType::JoyDual;
34constexpr std::array<u32, 10> npad_id_list{
35 0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
36};
37
38enum class JoystickId : std::size_t {
39 Joystick_Left,
40 Joystick_Right,
41};
42
43Controller_NPad::Controller_NPad() = default;
44Controller_NPad::~Controller_NPad() = default;
45
46void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
47 const auto controller_type = connected_controllers[controller_idx].type;
48 auto& controller = shared_memory_entries[controller_idx];
49 if (controller_type == NPadControllerType::None) {
50 return;
51 }
52 controller.joy_styles.raw = 0; // Zero out
53 controller.device_type.raw = 0;
54 switch (controller_type) {
55 case NPadControllerType::Handheld:
56 controller.joy_styles.handheld.Assign(1);
57 controller.device_type.handheld.Assign(1);
58 controller.pad_assignment = NPadAssignments::Dual;
59 break;
60 case NPadControllerType::JoyDual:
61 controller.joy_styles.joycon_dual.Assign(1);
62 controller.device_type.joycon_left.Assign(1);
63 controller.device_type.joycon_right.Assign(1);
64 controller.pad_assignment = NPadAssignments::Dual;
65 break;
66 case NPadControllerType::JoyLeft:
67 controller.joy_styles.joycon_left.Assign(1);
68 controller.device_type.joycon_left.Assign(1);
69 controller.pad_assignment = NPadAssignments::Dual;
70 break;
71 case NPadControllerType::JoyRight:
72 controller.joy_styles.joycon_right.Assign(1);
73 controller.device_type.joycon_right.Assign(1);
74 controller.pad_assignment = NPadAssignments::Dual;
75 break;
76 case NPadControllerType::Pokeball:
77 controller.joy_styles.pokeball.Assign(1);
78 controller.device_type.pokeball.Assign(1);
79 controller.pad_assignment = NPadAssignments::Single;
80 break;
81 case NPadControllerType::ProController:
82 controller.joy_styles.pro_controller.Assign(1);
83 controller.device_type.pro_controller.Assign(1);
84 controller.pad_assignment = NPadAssignments::Single;
85 break;
86 }
87
88 controller.single_color_error = ColorReadError::ReadOk;
89 controller.single_color.body_color = 0;
90 controller.single_color.button_color = 0;
91
92 controller.dual_color_error = ColorReadError::ReadOk;
93 controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
94 controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
95 controller.right_color.body_color = JOYCON_BODY_NEON_RED;
96 controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
97
98 controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
99 controller.properties.use_plus.Assign(1);
100 controller.properties.use_minus.Assign(1);
101 controller.battery_level[0] = BATTERY_FULL;
102 controller.battery_level[1] = BATTERY_FULL;
103 controller.battery_level[2] = BATTERY_FULL;
104}
105
106void Controller_NPad::OnInit() {
107 auto& kernel = Core::System::GetInstance().Kernel();
108 styleset_changed_event =
109 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
110
111 if (!IsControllerActivated()) {
112 return;
113 }
114
115 if (style.raw == 0) {
116 // We want to support all controllers
117 style.handheld.Assign(1);
118 style.joycon_left.Assign(1);
119 style.joycon_right.Assign(1);
120 style.joycon_dual.Assign(1);
121 style.pro_controller.Assign(1);
122 style.pokeball.Assign(1);
123 }
124 if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
125 [](const ControllerHolder& controller) { return controller.is_connected; })) {
126 supported_npad_id_types.resize(npad_id_list.size());
127 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
128 npad_id_list.size() * sizeof(u32));
129 AddNewController(PREFERRED_CONTROLLER);
130 }
131}
132
133void Controller_NPad::OnLoadInputDevices() {
134 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
135 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
136 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
137 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
138 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
139 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
140}
141
142void Controller_NPad::OnRelease() {}
143
144void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
145 if (!IsControllerActivated())
146 return;
147 for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
148 auto& npad = shared_memory_entries[i];
149 const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
150 &npad.handheld_states,
151 &npad.dual_states,
152 &npad.left_joy_states,
153 &npad.right_joy_states,
154 &npad.pokeball_states,
155 &npad.libnx};
156
157 for (auto* main_controller : controller_npads) {
158 main_controller->common.entry_count = 16;
159 main_controller->common.total_entry_count = 17;
160
161 const auto& last_entry =
162 main_controller->npad[main_controller->common.last_entry_index];
163
164 main_controller->common.timestamp = CoreTiming::GetTicks();
165 main_controller->common.last_entry_index =
166 (main_controller->common.last_entry_index + 1) % 17;
167
168 auto& cur_entry = main_controller->npad[main_controller->common.last_entry_index];
169
170 cur_entry.timestamp = last_entry.timestamp + 1;
171 cur_entry.timestamp2 = cur_entry.timestamp;
172 }
173
174 const auto& controller_type = connected_controllers[i].type;
175
176 if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
177 continue;
178 }
179
180 // Pad states
181 ControllerPadState pad_state{};
182 using namespace Settings::NativeButton;
183 pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
184 pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
185 pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
186 pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
187 pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
188 pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
189 pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
190 pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
191 pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
192 pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
193 pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
194 pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
195
196 pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
197 pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
198 pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
199 pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
200
201 pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
202 pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
203 pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
204 pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
205
206 pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
207 pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
208 pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
209 pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
210
211 pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
212 pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
213
214 AnalogPosition lstick_entry{};
215 AnalogPosition rstick_entry{};
216
217 const auto [stick_l_x_f, stick_l_y_f] =
218 sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
219 const auto [stick_r_x_f, stick_r_y_f] =
220 sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
221 lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
222 lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
223 rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
224 rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
225
226 if (controller_type == NPadControllerType::JoyLeft ||
227 controller_type == NPadControllerType::JoyRight) {
228 if (npad.properties.is_horizontal) {
229 ControllerPadState state{};
230 AnalogPosition temp_lstick_entry{};
231 AnalogPosition temp_rstick_entry{};
232 if (controller_type == NPadControllerType::JoyLeft) {
233 state.d_down.Assign(pad_state.d_left.Value());
234 state.d_left.Assign(pad_state.d_up.Value());
235 state.d_right.Assign(pad_state.d_down.Value());
236 state.d_up.Assign(pad_state.d_right.Value());
237 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
238 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
239
240 state.zl.Assign(pad_state.zl.Value());
241 state.plus.Assign(pad_state.minus.Value());
242
243 temp_lstick_entry = lstick_entry;
244 temp_rstick_entry = rstick_entry;
245 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
246 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
247 temp_lstick_entry.y *= -1;
248 } else if (controller_type == NPadControllerType::JoyRight) {
249 state.x.Assign(pad_state.a.Value());
250 state.a.Assign(pad_state.b.Value());
251 state.b.Assign(pad_state.y.Value());
252 state.y.Assign(pad_state.b.Value());
253
254 state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
255 state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
256 state.zr.Assign(pad_state.zr.Value());
257 state.plus.Assign(pad_state.plus.Value());
258
259 temp_lstick_entry = lstick_entry;
260 temp_rstick_entry = rstick_entry;
261 std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
262 std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
263 temp_rstick_entry.x *= -1;
264 }
265 pad_state.raw = state.raw;
266 lstick_entry = temp_lstick_entry;
267 rstick_entry = temp_rstick_entry;
268 }
269 }
270
271 auto& main_controller =
272 npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
273 auto& handheld_entry =
274 npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
275 auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
276 auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
277 auto& right_entry =
278 npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
279 auto& pokeball_entry =
280 npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
281 auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
282
283 if (hold_type == NpadHoldType::Horizontal) {
284 // TODO(ogniK): Remap buttons for different orientations
285 }
286 libnx_entry.connection_status.raw = 0;
287
288 switch (controller_type) {
289 case NPadControllerType::Handheld:
290 handheld_entry.connection_status.raw = 0;
291 handheld_entry.connection_status.IsConnected.Assign(1);
292 if (!Settings::values.use_docked_mode) {
293 handheld_entry.connection_status.IsWired.Assign(1);
294 }
295 handheld_entry.pad_states.raw = pad_state.raw;
296 handheld_entry.l_stick = lstick_entry;
297 handheld_entry.r_stick = rstick_entry;
298 break;
299 case NPadControllerType::JoyDual:
300 dual_entry.connection_status.raw = 0;
301
302 dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
303 dual_entry.connection_status.IsRightJoyConnected.Assign(1);
304 dual_entry.connection_status.IsConnected.Assign(1);
305
306 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
307 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
308 libnx_entry.connection_status.IsConnected.Assign(1);
309
310 dual_entry.pad_states.raw = pad_state.raw;
311 dual_entry.l_stick = lstick_entry;
312 dual_entry.r_stick = rstick_entry;
313 case NPadControllerType::JoyLeft:
314 left_entry.connection_status.raw = 0;
315
316 left_entry.connection_status.IsConnected.Assign(1);
317 left_entry.pad_states.raw = pad_state.raw;
318 left_entry.l_stick = lstick_entry;
319 left_entry.r_stick = rstick_entry;
320 break;
321 case NPadControllerType::JoyRight:
322 right_entry.connection_status.raw = 0;
323
324 right_entry.connection_status.IsConnected.Assign(1);
325 right_entry.pad_states.raw = pad_state.raw;
326 right_entry.l_stick = lstick_entry;
327 right_entry.r_stick = rstick_entry;
328 break;
329 case NPadControllerType::Pokeball:
330 pokeball_entry.connection_status.raw = 0;
331
332 pokeball_entry.connection_status.IsConnected.Assign(1);
333 pokeball_entry.connection_status.IsWired.Assign(1);
334
335 pokeball_entry.pad_states.raw = pad_state.raw;
336 pokeball_entry.l_stick = lstick_entry;
337 pokeball_entry.r_stick = rstick_entry;
338 break;
339 case NPadControllerType::ProController:
340 main_controller.connection_status.raw = 0;
341
342 main_controller.connection_status.IsConnected.Assign(1);
343 main_controller.connection_status.IsWired.Assign(1);
344 main_controller.pad_states.raw = pad_state.raw;
345 main_controller.l_stick = lstick_entry;
346 main_controller.r_stick = rstick_entry;
347 break;
348 }
349
350 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
351 // any controllers.
352 libnx_entry.pad_states.raw = pad_state.raw;
353 libnx_entry.l_stick = lstick_entry;
354 libnx_entry.r_stick = rstick_entry;
355 }
356 std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
357 shared_memory_entries.size() * sizeof(NPadEntry));
358} // namespace Service::HID
359
360void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
361 style.raw = style_set.raw;
362}
363
364Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
365 return style;
366}
367
368void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
369 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
370 supported_npad_id_types.clear();
371 supported_npad_id_types.resize(length / sizeof(u32));
372 std::memcpy(supported_npad_id_types.data(), data, length);
373 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
374 auto& controller = connected_controllers[i];
375 if (!controller.is_connected) {
376 continue;
377 }
378 if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
379 controller.type = DecideBestController(PREFERRED_CONTROLLER);
380 InitNewlyAddedControler(i);
381 }
382 }
383}
384
385void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
386 ASSERT(max_length < supported_npad_id_types.size());
387 std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
388}
389
390std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
391 return supported_npad_id_types.size();
392}
393
394void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
395 hold_type = joy_hold_type;
396}
397Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
398 return hold_type;
399}
400
401void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
402 ASSERT(npad_id < shared_memory_entries.size());
403 shared_memory_entries[npad_id].pad_assignment = assignment_mode;
404}
405
406void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
407 const std::vector<Vibration>& vibrations) {
408 if (!can_controllers_vibrate) {
409 return;
410 }
411 for (std::size_t i = 0; i < controller_ids.size(); i++) {
412 std::size_t controller_pos = i;
413 // Handheld controller conversion
414 if (controller_pos == NPAD_HANDHELD) {
415 controller_pos = 8;
416 }
417 // Unknown controller conversion
418 if (controller_pos == NPAD_UNKNOWN) {
419 controller_pos = 9;
420 }
421 if (connected_controllers[controller_pos].is_connected) {
422 // TODO(ogniK): Vibrate the physical controller
423 }
424 }
425 LOG_WARNING(Service_HID, "(STUBBED) called");
426 last_processed_vibration = vibrations.back();
427}
428
429Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
430 return styleset_changed_event;
431}
432
433Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
434 return last_processed_vibration;
435}
436void Controller_NPad::AddNewController(NPadControllerType controller) {
437 if (controller == NPadControllerType::Handheld) {
438 connected_controllers[8] = {controller, true};
439 InitNewlyAddedControler(8);
440 return;
441 }
442 const auto pos =
443 std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
444 [](const ControllerHolder& holder) { return !holder.is_connected; });
445 if (pos == connected_controllers.end() - 2) {
446 LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
447 return;
448 }
449 const auto controller_id = std::distance(connected_controllers.begin(), pos);
450 connected_controllers[controller_id] = {controller, true};
451 InitNewlyAddedControler(controller_id);
452}
453
454void Controller_NPad::ConnectNPad(u32 npad_id) {
455 if (npad_id >= connected_controllers.size())
456 return;
457 connected_controllers[npad_id].is_connected = true;
458}
459
460void Controller_NPad::DisconnectNPad(u32 npad_id) {
461 if (npad_id >= connected_controllers.size())
462 return;
463 connected_controllers[npad_id].is_connected = false;
464}
465
466Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
467 if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
468 // These are controllers without led patterns
469 return LedPattern{0, 0, 0, 0};
470 }
471 switch (npad_id) {
472 case 0:
473 return LedPattern{1, 0, 0, 0};
474 case 1:
475 return LedPattern{0, 1, 0, 0};
476 case 2:
477 return LedPattern{0, 0, 1, 0};
478 case 3:
479 return LedPattern{0, 0, 0, 1};
480 case 4:
481 return LedPattern{1, 0, 0, 1};
482 case 5:
483 return LedPattern{1, 0, 1, 0};
484 case 6:
485 return LedPattern{1, 0, 1, 1};
486 case 7:
487 return LedPattern{0, 1, 1, 0};
488 default:
489 UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
490 return LedPattern{0, 0, 0, 0};
491 };
492}
493void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
494 can_controllers_vibrate = can_vibrate;
495}
496
497bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
498 const bool support_handheld =
499 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
500 supported_npad_id_types.end();
501 if (controller == NPadControllerType::Handheld) {
502 // Handheld is not even a supported type, lets stop here
503 if (!support_handheld) {
504 return false;
505 }
506 // Handheld should not be supported in docked mode
507 if (Settings::values.use_docked_mode) {
508 return false;
509 }
510
511 return true;
512 }
513 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(),
514 [](u32 npad_id) { return npad_id <= MAX_NPAD_ID; })) {
515 switch (controller) {
516 case NPadControllerType::ProController:
517 return style.pro_controller;
518 case NPadControllerType::JoyDual:
519 return style.joycon_dual;
520 case NPadControllerType::JoyLeft:
521 return style.joycon_left;
522 case NPadControllerType::JoyRight:
523 return style.joycon_right;
524 case NPadControllerType::Pokeball:
525 return style.pokeball;
526 default:
527 return false;
528 }
529 }
530 return false;
531}
532
533Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
534 NPadControllerType priority) const {
535 if (IsControllerSupported(priority)) {
536 return priority;
537 }
538 const auto is_docked = Settings::values.use_docked_mode;
539 if (is_docked && priority == NPadControllerType::Handheld) {
540 priority = NPadControllerType::JoyDual;
541 if (IsControllerSupported(priority)) {
542 return priority;
543 }
544 }
545 std::vector<NPadControllerType> priority_list;
546 switch (priority) {
547 case NPadControllerType::ProController:
548 priority_list.push_back(NPadControllerType::JoyDual);
549 if (!is_docked) {
550 priority_list.push_back(NPadControllerType::Handheld);
551 }
552 priority_list.push_back(NPadControllerType::JoyLeft);
553 priority_list.push_back(NPadControllerType::JoyRight);
554 priority_list.push_back(NPadControllerType::Pokeball);
555 break;
556 case NPadControllerType::Handheld:
557 priority_list.push_back(NPadControllerType::JoyDual);
558 priority_list.push_back(NPadControllerType::ProController);
559 priority_list.push_back(NPadControllerType::JoyLeft);
560 priority_list.push_back(NPadControllerType::JoyRight);
561 priority_list.push_back(NPadControllerType::Pokeball);
562 break;
563 case NPadControllerType::JoyDual:
564 if (!is_docked) {
565 priority_list.push_back(NPadControllerType::Handheld);
566 }
567 priority_list.push_back(NPadControllerType::ProController);
568 priority_list.push_back(NPadControllerType::JoyLeft);
569 priority_list.push_back(NPadControllerType::JoyRight);
570 priority_list.push_back(NPadControllerType::Pokeball);
571 break;
572 case NPadControllerType::JoyLeft:
573 priority_list.push_back(NPadControllerType::JoyRight);
574 priority_list.push_back(NPadControllerType::JoyDual);
575 if (!is_docked) {
576 priority_list.push_back(NPadControllerType::Handheld);
577 }
578 priority_list.push_back(NPadControllerType::ProController);
579 priority_list.push_back(NPadControllerType::Pokeball);
580 break;
581 case NPadControllerType::JoyRight:
582 priority_list.push_back(NPadControllerType::JoyLeft);
583 priority_list.push_back(NPadControllerType::JoyDual);
584 if (!is_docked) {
585 priority_list.push_back(NPadControllerType::Handheld);
586 }
587 priority_list.push_back(NPadControllerType::ProController);
588 priority_list.push_back(NPadControllerType::Pokeball);
589 break;
590 case NPadControllerType::Pokeball:
591 priority_list.push_back(NPadControllerType::JoyLeft);
592 priority_list.push_back(NPadControllerType::JoyRight);
593 priority_list.push_back(NPadControllerType::JoyDual);
594 if (!is_docked) {
595 priority_list.push_back(NPadControllerType::Handheld);
596 }
597 priority_list.push_back(NPadControllerType::ProController);
598 break;
599 default:
600 priority_list.push_back(NPadControllerType::JoyDual);
601 if (!is_docked) {
602 priority_list.push_back(NPadControllerType::Handheld);
603 }
604 priority_list.push_back(NPadControllerType::ProController);
605 priority_list.push_back(NPadControllerType::JoyLeft);
606 priority_list.push_back(NPadControllerType::JoyRight);
607 priority_list.push_back(NPadControllerType::JoyDual);
608 }
609
610 const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
611 [this](auto type) { return IsControllerSupported(type); });
612 if (iter == priority_list.end()) {
613 UNIMPLEMENTED_MSG("Could not find supported controller!");
614 return priority;
615 }
616
617 return *iter;
618}
619
620} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
new file mode 100644
index 000000000..ac86985ff
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -0,0 +1,289 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11#include "core/settings.h"
12
13namespace Service::HID {
14
15class Controller_NPad final : public ControllerBase {
16public:
17 Controller_NPad();
18 ~Controller_NPad() override;
19
20 // Called when the controller is initialized
21 void OnInit() override;
22
23 // When the controller is released
24 void OnRelease() override;
25
26 // When the controller is requesting an update for the shared memory
27 void OnUpdate(u8* data, std::size_t size) override;
28
29 // Called when input devices should be loaded
30 void OnLoadInputDevices() override;
31
32 struct NPadType {
33 union {
34 u32_le raw{};
35
36 BitField<0, 1, u32_le> pro_controller;
37 BitField<1, 1, u32_le> handheld;
38 BitField<2, 1, u32_le> joycon_dual;
39 BitField<3, 1, u32_le> joycon_left;
40 BitField<4, 1, u32_le> joycon_right;
41
42 BitField<6, 1, u32_le> pokeball; // TODO(ogniK): Confirm when possible
43 };
44 };
45 static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
46
47 struct Vibration {
48 f32 amp_low;
49 f32 freq_low;
50 f32 amp_high;
51 f32 freq_high;
52 };
53 static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
54
55 enum class NpadHoldType : u64 {
56 Vertical = 0,
57 Horizontal = 1,
58 };
59
60 enum class NPadAssignments : u32_le {
61 Dual = 0,
62 Single = 1,
63 };
64
65 enum class NPadControllerType {
66 None,
67 ProController,
68 Handheld,
69 JoyDual,
70 JoyLeft,
71 JoyRight,
72 Pokeball,
73 };
74
75 struct LedPattern {
76 explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
77 position1.Assign(light1);
78 position1.Assign(light2);
79 position1.Assign(light3);
80 position1.Assign(light4);
81 }
82 union {
83 u64 raw{};
84 BitField<0, 1, u64> position1;
85 BitField<1, 1, u64> position2;
86 BitField<2, 1, u64> position3;
87 BitField<3, 1, u64> position4;
88 };
89 };
90
91 void SetSupportedStyleSet(NPadType style_set);
92 NPadType GetSupportedStyleSet() const;
93
94 void SetSupportedNPadIdTypes(u8* data, std::size_t length);
95 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
96 std::size_t GetSupportedNPadIdTypesSize() const;
97
98 void SetHoldType(NpadHoldType joy_hold_type);
99 NpadHoldType GetHoldType() const;
100
101 void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
102
103 void VibrateController(const std::vector<u32>& controller_ids,
104 const std::vector<Vibration>& vibrations);
105
106 Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
107 Vibration GetLastVibration() const;
108
109 void AddNewController(NPadControllerType controller);
110
111 void ConnectNPad(u32 npad_id);
112 void DisconnectNPad(u32 npad_id);
113 LedPattern GetLedPattern(u32 npad_id);
114 void SetVibrationEnabled(bool can_vibrate);
115
116private:
117 struct CommonHeader {
118 s64_le timestamp;
119 s64_le total_entry_count;
120 s64_le last_entry_index;
121 s64_le entry_count;
122 };
123 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
124
125 struct ControllerColor {
126 u32_le body_color;
127 u32_le button_color;
128 };
129 static_assert(sizeof(ControllerColor) == 8, "ControllerColor is an invalid size");
130
131 struct ControllerPadState {
132 union {
133 u64_le raw{};
134 // Button states
135 BitField<0, 1, u64_le> a;
136 BitField<1, 1, u64_le> b;
137 BitField<2, 1, u64_le> x;
138 BitField<3, 1, u64_le> y;
139 BitField<4, 1, u64_le> l_stick;
140 BitField<5, 1, u64_le> r_stick;
141 BitField<6, 1, u64_le> l;
142 BitField<7, 1, u64_le> r;
143 BitField<8, 1, u64_le> zl;
144 BitField<9, 1, u64_le> zr;
145 BitField<10, 1, u64_le> plus;
146 BitField<11, 1, u64_le> minus;
147
148 // D-Pad
149 BitField<12, 1, u64_le> d_left;
150 BitField<13, 1, u64_le> d_up;
151 BitField<14, 1, u64_le> d_right;
152 BitField<15, 1, u64_le> d_down;
153
154 // Left JoyStick
155 BitField<16, 1, u64_le> l_stick_left;
156 BitField<17, 1, u64_le> l_stick_up;
157 BitField<18, 1, u64_le> l_stick_right;
158 BitField<19, 1, u64_le> l_stick_down;
159
160 // Right JoyStick
161 BitField<20, 1, u64_le> r_stick_left;
162 BitField<21, 1, u64_le> r_stick_up;
163 BitField<22, 1, u64_le> r_stick_right;
164 BitField<23, 1, u64_le> r_stick_down;
165
166 // Not always active?
167 BitField<24, 1, u64_le> sl;
168 BitField<25, 1, u64_le> sr;
169 };
170 };
171 static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
172
173 struct AnalogPosition {
174 s32_le x;
175 s32_le y;
176 };
177 static_assert(sizeof(AnalogPosition) == 8, "AnalogPosition is an invalid size");
178
179 struct ConnectionState {
180 union {
181 u32_le raw{};
182 BitField<0, 1, u32_le> IsConnected;
183 BitField<1, 1, u32_le> IsWired;
184 BitField<2, 1, u32_le> IsLeftJoyConnected;
185 BitField<3, 1, u32_le> IsLeftJoyWired;
186 BitField<4, 1, u32_le> IsRightJoyConnected;
187 BitField<5, 1, u32_le> IsRightJoyWired;
188 };
189 };
190 static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
191
192 struct GenericStates {
193 s64_le timestamp;
194 s64_le timestamp2;
195 ControllerPadState pad_states;
196 AnalogPosition l_stick;
197 AnalogPosition r_stick;
198 ConnectionState connection_status;
199 };
200 static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
201
202 struct NPadGeneric {
203 CommonHeader common;
204 std::array<GenericStates, 17> npad;
205 };
206 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
207
208 enum class ColorReadError : u32_le {
209 ReadOk = 0,
210 ColorDoesntExist = 1,
211 NoController = 2,
212 };
213
214 struct NPadProperties {
215 union {
216 s64_le raw{};
217 BitField<11, 1, s64_le> is_vertical;
218 BitField<12, 1, s64_le> is_horizontal;
219 BitField<13, 1, s64_le> use_plus;
220 BitField<14, 1, s64_le> use_minus;
221 };
222 };
223
224 struct NPadDevice {
225 union {
226 u32_le raw{};
227 BitField<0, 1, s32_le> pro_controller;
228 BitField<1, 1, s32_le> handheld;
229 BitField<2, 1, s32_le> handheld_left;
230 BitField<3, 1, s32_le> handheld_right;
231 BitField<4, 1, s32_le> joycon_left;
232 BitField<5, 1, s32_le> joycon_right;
233 BitField<6, 1, s32_le> pokeball;
234 };
235 };
236
237 struct NPadEntry {
238 NPadType joy_styles;
239 NPadAssignments pad_assignment;
240
241 ColorReadError single_color_error;
242 ControllerColor single_color;
243
244 ColorReadError dual_color_error;
245 ControllerColor left_color;
246 ControllerColor right_color;
247
248 NPadGeneric main_controller_states;
249 NPadGeneric handheld_states;
250 NPadGeneric dual_states;
251 NPadGeneric left_joy_states;
252 NPadGeneric right_joy_states;
253 NPadGeneric pokeball_states;
254 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
255 // relying on this for the time being
256 INSERT_PADDING_BYTES(
257 0x708 *
258 6); // TODO(ogniK): SixAxis states, require more information before implementation
259 NPadDevice device_type;
260 NPadProperties properties;
261 INSERT_PADDING_WORDS(1);
262 std::array<u32, 3> battery_level;
263 INSERT_PADDING_BYTES(0x5c);
264 INSERT_PADDING_BYTES(0xdf8);
265 };
266 static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
267
268 struct ControllerHolder {
269 Controller_NPad::NPadControllerType type;
270 bool is_connected;
271 };
272
273 NPadType style{};
274 std::array<NPadEntry, 10> shared_memory_entries{};
275 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
276 buttons;
277 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
278 std::vector<u32> supported_npad_id_types{};
279 NpadHoldType hold_type{NpadHoldType::Vertical};
280 Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
281 Vibration last_processed_vibration{};
282 std::array<ControllerHolder, 10> connected_controllers{};
283 bool can_controllers_vibrate{true};
284
285 void InitNewlyAddedControler(std::size_t controller_idx);
286 bool IsControllerSupported(NPadControllerType controller) const;
287 NPadControllerType DecideBestController(NPadControllerType priority) const;
288};
289} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
new file mode 100644
index 000000000..02fcfadd9
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -0,0 +1,39 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/stubbed.h"
9
10namespace Service::HID {
11
12Controller_Stubbed::Controller_Stubbed() = default;
13Controller_Stubbed::~Controller_Stubbed() = default;
14
15void Controller_Stubbed::OnInit() {}
16
17void Controller_Stubbed::OnRelease() {}
18
19void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
20 if (!smart_update) {
21 return;
22 }
23
24 CommonHeader header{};
25 header.timestamp = CoreTiming::GetTicks();
26 header.total_entry_count = 17;
27 header.entry_count = 0;
28 header.last_entry_index = 0;
29
30 std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
31}
32
33void Controller_Stubbed::OnLoadInputDevices() {}
34
35void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
36 common_offset = off;
37 smart_update = true;
38}
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
new file mode 100644
index 000000000..4a21c643e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -0,0 +1,34 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/controllers/controller_base.h"
9
10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase {
12public:
13 Controller_Stubbed();
14 ~Controller_Stubbed() override;
15
16 // Called when the controller is initialized
17 void OnInit() override;
18
19 // When the controller is released
20 void OnRelease() override;
21
22 // When the controller is requesting an update for the shared memory
23 void OnUpdate(u8* data, std::size_t size) override;
24
25 // Called when input devices should be loaded
26 void OnLoadInputDevices() override;
27
28 void SetCommonHeaderOffset(std::size_t off);
29
30private:
31 bool smart_update{};
32 std::size_t common_offset{};
33};
34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
new file mode 100644
index 000000000..43efef803
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -0,0 +1,65 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/frontend/emu_window.h"
9#include "core/frontend/input.h"
10#include "core/hle/service/hid/controllers/touchscreen.h"
11#include "core/settings.h"
12
13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
15
16Controller_Touchscreen::Controller_Touchscreen() = default;
17Controller_Touchscreen::~Controller_Touchscreen() = default;
18
19void Controller_Touchscreen::OnInit() {}
20
21void Controller_Touchscreen::OnRelease() {}
22
23void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
24 shared_memory.header.timestamp = CoreTiming::GetTicks();
25 shared_memory.header.total_entry_count = 17;
26
27 if (!IsControllerActivated()) {
28 shared_memory.header.entry_count = 0;
29 shared_memory.header.last_entry_index = 0;
30 return;
31 }
32 shared_memory.header.entry_count = 16;
33
34 const auto& last_entry =
35 shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
36 shared_memory.header.last_entry_index = (shared_memory.header.last_entry_index + 1) % 17;
37 auto& cur_entry = shared_memory.shared_memory_entries[shared_memory.header.last_entry_index];
38
39 cur_entry.sampling_number = last_entry.sampling_number + 1;
40 cur_entry.sampling_number2 = cur_entry.sampling_number;
41
42 const auto [x, y, pressed] = touch_device->GetStatus();
43 auto& touch_entry = cur_entry.states[0];
44 if (pressed) {
45 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
46 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
47 touch_entry.diameter_x = 15;
48 touch_entry.diameter_y = 15;
49 touch_entry.rotation_angle = 0;
50 const u64 tick = CoreTiming::GetTicks();
51 touch_entry.delta_time = tick - last_touch;
52 last_touch = tick;
53 touch_entry.finger = 0;
54 cur_entry.entry_count = 1;
55 } else {
56 cur_entry.entry_count = 0;
57 }
58
59 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(TouchScreenSharedMemory));
60}
61
62void Controller_Touchscreen::OnLoadInputDevices() {
63 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
64}
65} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
new file mode 100644
index 000000000..e5db6e6ba
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -0,0 +1,63 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/frontend/input.h"
11#include "core/hle/service/hid/controllers/controller_base.h"
12
13namespace Service::HID {
14class Controller_Touchscreen final : public ControllerBase {
15public:
16 Controller_Touchscreen();
17 ~Controller_Touchscreen() override;
18
19 // Called when the controller is initialized
20 void OnInit() override;
21
22 // When the controller is released
23 void OnRelease() override;
24
25 // When the controller is requesting an update for the shared memory
26 void OnUpdate(u8* data, std::size_t size) override;
27
28 // Called when input devices should be loaded
29 void OnLoadInputDevices() override;
30
31private:
32 struct TouchState {
33 u64_le delta_time;
34 u32_le attribute;
35 u32_le finger;
36 u32_le x;
37 u32_le y;
38 u32_le diameter_x;
39 u32_le diameter_y;
40 u32_le rotation_angle;
41 };
42 static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
43
44 struct TouchScreenEntry {
45 s64_le sampling_number;
46 s64_le sampling_number2;
47 s32_le entry_count;
48 std::array<TouchState, 16> states;
49 };
50 static_assert(sizeof(TouchScreenEntry) == 0x298, "TouchScreenEntry is an invalid size");
51
52 struct TouchScreenSharedMemory {
53 CommonHeader header;
54 std::array<TouchScreenEntry, 17> shared_memory_entries{};
55 INSERT_PADDING_BYTES(0x3c8);
56 };
57 static_assert(sizeof(TouchScreenSharedMemory) == 0x3000,
58 "TouchScreenSharedMemory is an invalid size");
59 TouchScreenSharedMemory shared_memory{};
60 std::unique_ptr<Input::TouchDevice> touch_device;
61 s64_le last_touch{};
62};
63} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
new file mode 100644
index 000000000..cd397c70b
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -0,0 +1,45 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "common/common_types.h"
7#include "core/core_timing.h"
8#include "core/hle/service/hid/controllers/xpad.h"
9
10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12
13Controller_XPad::Controller_XPad() = default;
14Controller_XPad::~Controller_XPad() = default;
15
16void Controller_XPad::OnInit() {}
17
18void Controller_XPad::OnRelease() {}
19
20void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
21 for (auto& xpad_entry : shared_memory.shared_memory_entries) {
22 xpad_entry.header.timestamp = CoreTiming::GetTicks();
23 xpad_entry.header.total_entry_count = 17;
24
25 if (!IsControllerActivated()) {
26 xpad_entry.header.entry_count = 0;
27 xpad_entry.header.last_entry_index = 0;
28 return;
29 }
30 xpad_entry.header.entry_count = 16;
31
32 const auto& last_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
33 xpad_entry.header.last_entry_index = (xpad_entry.header.last_entry_index + 1) % 17;
34 auto& cur_entry = xpad_entry.pad_states[xpad_entry.header.last_entry_index];
35
36 cur_entry.sampling_number = last_entry.sampling_number + 1;
37 cur_entry.sampling_number2 = cur_entry.sampling_number;
38 }
39 // TODO(ogniK): Update xpad states
40
41 std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
42}
43
44void Controller_XPad::OnLoadInputDevices() {}
45} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
new file mode 100644
index 000000000..ff836989f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -0,0 +1,60 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10#include "core/hle/service/hid/controllers/controller_base.h"
11
12namespace Service::HID {
13class Controller_XPad final : public ControllerBase {
14public:
15 Controller_XPad();
16 ~Controller_XPad() override;
17
18 // Called when the controller is initialized
19 void OnInit() override;
20
21 // When the controller is released
22 void OnRelease() override;
23
24 // When the controller is requesting an update for the shared memory
25 void OnUpdate(u8* data, std::size_t size) override;
26
27 // Called when input devices should be loaded
28 void OnLoadInputDevices() override;
29
30private:
31 struct AnalogStick {
32 s32_le x;
33 s32_le y;
34 };
35 static_assert(sizeof(AnalogStick) == 0x8, "AnalogStick is an invalid size");
36
37 struct XPadState {
38 s64_le sampling_number;
39 s64_le sampling_number2;
40 s32_le attributes;
41 u32_le pad_states;
42 AnalogStick x_stick;
43 AnalogStick y_stick;
44 };
45 static_assert(sizeof(XPadState) == 0x28, "XPadState is an invalid size");
46
47 struct XPadEntry {
48 CommonHeader header;
49 std::array<XPadState, 17> pad_states{};
50 INSERT_PADDING_BYTES(0x138);
51 };
52 static_assert(sizeof(XPadEntry) == 0x400, "XPadEntry is an invalid size");
53
54 struct SharedMemory {
55 std::array<XPadEntry, 4> shared_memory_entries{};
56 };
57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
58 SharedMemory shared_memory{};
59};
60} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 7c6b0a4e6..a9aa9ec78 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include "common/common_types.h"
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/core_timing.h" 9#include "core/core_timing.h"
@@ -19,6 +21,16 @@
19#include "core/hle/service/service.h" 21#include "core/hle/service/service.h"
20#include "core/settings.h" 22#include "core/settings.h"
21 23
24#include "core/hle/service/hid/controllers/controller_base.h"
25#include "core/hle/service/hid/controllers/debug_pad.h"
26#include "core/hle/service/hid/controllers/gesture.h"
27#include "core/hle/service/hid/controllers/keyboard.h"
28#include "core/hle/service/hid/controllers/mouse.h"
29#include "core/hle/service/hid/controllers/npad.h"
30#include "core/hle/service/hid/controllers/stubbed.h"
31#include "core/hle/service/hid/controllers/touchscreen.h"
32#include "core/hle/service/hid/controllers/xpad.h"
33
22namespace Service::HID { 34namespace Service::HID {
23 35
24// Updating period for each HID device. 36// Updating period for each HID device.
@@ -26,6 +38,22 @@ namespace Service::HID {
26constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 38constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
27constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 39constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
28constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100; 40constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
41constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
42enum class HidController : std::size_t {
43 DebugPad,
44 Touchscreen,
45 Mouse,
46 Keyboard,
47 XPad,
48 Unknown1,
49 Unknown2,
50 Unknown3,
51 SixAxisSensor,
52 NPad,
53 Gesture,
54
55 MaxControllers,
56};
29 57
30class IAppletResource final : public ServiceFramework<IAppletResource> { 58class IAppletResource final : public ServiceFramework<IAppletResource> {
31public: 59public:
@@ -37,19 +65,57 @@ public:
37 65
38 auto& kernel = Core::System::GetInstance().Kernel(); 66 auto& kernel = Core::System::GetInstance().Kernel();
39 shared_mem = Kernel::SharedMemory::Create( 67 shared_mem = Kernel::SharedMemory::Create(
40 kernel, nullptr, 0x40000, Kernel::MemoryPermission::ReadWrite, 68 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
41 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); 69 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
42 70
71 MakeController<Controller_DebugPad>(HidController::DebugPad);
72 MakeController<Controller_Touchscreen>(HidController::Touchscreen);
73 MakeController<Controller_Mouse>(HidController::Mouse);
74 MakeController<Controller_Keyboard>(HidController::Keyboard);
75 MakeController<Controller_XPad>(HidController::XPad);
76 MakeController<Controller_Stubbed>(HidController::Unknown1);
77 MakeController<Controller_Stubbed>(HidController::Unknown2);
78 MakeController<Controller_Stubbed>(HidController::Unknown3);
79 MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
80 MakeController<Controller_NPad>(HidController::NPad);
81 MakeController<Controller_Gesture>(HidController::Gesture);
82
83 // Homebrew doesn't try to activate some controllers, so we activate them by default
84 GetController<Controller_NPad>(HidController::NPad).ActivateController();
85 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
86
87 GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
88 GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
89 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
90
43 // Register update callbacks 91 // Register update callbacks
44 pad_update_event = CoreTiming::RegisterEvent( 92 pad_update_event = CoreTiming::RegisterEvent(
45 "HID::UpdatePadCallback", 93 "HID::UpdatePadCallback",
46 [this](u64 userdata, int cycles_late) { UpdatePadCallback(userdata, cycles_late); }); 94 [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
47 95
48 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 96 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
49 97
50 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); 98 CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
51 } 99 }
52 100
101 void ActivateController(HidController controller) {
102 controllers[static_cast<size_t>(controller)]->ActivateController();
103 }
104
105 void DeactivateController(HidController controller) {
106 controllers[static_cast<size_t>(controller)]->DeactivateController();
107 }
108
109 template <typename T>
110 void MakeController(HidController controller) {
111 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
112 }
113
114 template <typename T>
115 T& GetController(HidController controller) {
116 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
117 }
118
53 ~IAppletResource() { 119 ~IAppletResource() {
54 CoreTiming::UnscheduleEvent(pad_update_event, 0); 120 CoreTiming::UnscheduleEvent(pad_update_event, 0);
55 } 121 }
@@ -62,200 +128,15 @@ private:
62 LOG_DEBUG(Service_HID, "called"); 128 LOG_DEBUG(Service_HID, "called");
63 } 129 }
64 130
65 void LoadInputDevices() { 131 void UpdateControllers(u64 userdata, int cycles_late) {
66 std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, 132 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
67 Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, 133 for (const auto& controller : controllers) {
68 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 134 if (should_reload) {
69 std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 135 controller->OnLoadInputDevices();
70 Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
71 sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
72 touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
73 // TODO(shinyquagsire23): gyro, mouse, keyboard
74 }
75
76 void UpdatePadCallback(u64 userdata, int cycles_late) {
77 SharedMemory mem{};
78 std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
79
80 if (Settings::values.is_device_reload_pending.exchange(false))
81 LoadInputDevices();
82
83 // Set up controllers as neon red+blue Joy-Con attached to console
84 ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
85 controller_header.type = ControllerType_Handheld;
86 controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
87 controller_header.right_color_body = JOYCON_BODY_NEON_RED;
88 controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED;
89 controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
90 controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
91
92 for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) {
93 for (auto& layout : mem.controllers[controller].layouts) {
94 layout.header.num_entries = HID_NUM_ENTRIES;
95 layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
96
97 // HID shared memory stores the state of the past 17 samples in a circlular buffer,
98 // each with a timestamp in number of samples since boot.
99 const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry];
100
101 layout.header.timestamp_ticks = CoreTiming::GetTicks();
102 layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
103
104 ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
105 entry.timestamp = last_entry.timestamp + 1;
106 // TODO(shinyquagsire23): Is this always identical to timestamp?
107 entry.timestamp_2 = entry.timestamp;
108
109 // TODO(shinyquagsire23): More than just handheld input
110 if (controller != Controller_Handheld)
111 continue;
112
113 entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
114
115 // TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
116 // For now everything is just the default handheld layout, but split Joy-Con will
117 // rotate the face buttons and directions for certain layouts.
118 ControllerPadState& state = entry.buttons;
119 using namespace Settings::NativeButton;
120 state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
121 state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
122 state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
123 state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
124 state.lstick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
125 state.rstick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
126 state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
127 state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
128 state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
129 state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
130 state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
131 state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
132
133 state.dleft.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
134 state.dup.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
135 state.dright.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
136 state.ddown.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
137
138 state.lstick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
139 state.lstick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
140 state.lstick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
141 state.lstick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
142
143 state.rstick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
144 state.rstick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
145 state.rstick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
146 state.rstick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
147
148 state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
149 state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
150
151 const auto [stick_l_x_f, stick_l_y_f] = sticks[Joystick_Left]->GetStatus();
152 const auto [stick_r_x_f, stick_r_y_f] = sticks[Joystick_Right]->GetStatus();
153 entry.joystick_left_x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
154 entry.joystick_left_y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
155 entry.joystick_right_x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
156 entry.joystick_right_y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
157 } 136 }
137 controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
158 } 138 }
159 139
160 TouchScreen& touchscreen = mem.touchscreen;
161 const u64 last_entry = touchscreen.header.latest_entry;
162 const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
163 const u64 timestamp = CoreTiming::GetTicks();
164 const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
165 touchscreen.header.timestamp_ticks = timestamp;
166 touchscreen.header.num_entries = touchscreen.entries.size();
167 touchscreen.header.latest_entry = curr_entry;
168 touchscreen.header.max_entry_index = touchscreen.entries.size();
169 touchscreen.header.timestamp = timestamp;
170 touchscreen.entries[curr_entry].header.timestamp = sample_counter;
171
172 TouchScreenEntryTouch touch_entry{};
173 auto [x, y, pressed] = touch_device->GetStatus();
174 touch_entry.timestamp = timestamp;
175 touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
176 touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
177 touch_entry.touch_index = 0;
178
179 // TODO(DarkLordZach): Maybe try to derive these from EmuWindow?
180 touch_entry.diameter_x = 15;
181 touch_entry.diameter_y = 15;
182 touch_entry.angle = 0;
183
184 // TODO(DarkLordZach): Implement multi-touch support
185 if (pressed) {
186 touchscreen.entries[curr_entry].header.num_touches = 1;
187 touchscreen.entries[curr_entry].touches[0] = touch_entry;
188 } else {
189 touchscreen.entries[curr_entry].header.num_touches = 0;
190 }
191
192 // TODO(shinyquagsire23): Properly implement mouse
193 Mouse& mouse = mem.mouse;
194 const u64 last_mouse_entry = mouse.header.latest_entry;
195 const u64 curr_mouse_entry = (mouse.header.latest_entry + 1) % mouse.entries.size();
196 const u64 mouse_sample_counter = mouse.entries[last_mouse_entry].timestamp + 1;
197 mouse.header.timestamp_ticks = timestamp;
198 mouse.header.num_entries = mouse.entries.size();
199 mouse.header.max_entry_index = mouse.entries.size();
200 mouse.header.latest_entry = curr_mouse_entry;
201
202 mouse.entries[curr_mouse_entry].timestamp = mouse_sample_counter;
203 mouse.entries[curr_mouse_entry].timestamp_2 = mouse_sample_counter;
204
205 // TODO(shinyquagsire23): Properly implement keyboard
206 Keyboard& keyboard = mem.keyboard;
207 const u64 last_keyboard_entry = keyboard.header.latest_entry;
208 const u64 curr_keyboard_entry =
209 (keyboard.header.latest_entry + 1) % keyboard.entries.size();
210 const u64 keyboard_sample_counter = keyboard.entries[last_keyboard_entry].timestamp + 1;
211 keyboard.header.timestamp_ticks = timestamp;
212 keyboard.header.num_entries = keyboard.entries.size();
213 keyboard.header.latest_entry = last_keyboard_entry;
214 keyboard.header.max_entry_index = keyboard.entries.size();
215
216 keyboard.entries[curr_keyboard_entry].timestamp = keyboard_sample_counter;
217 keyboard.entries[curr_keyboard_entry].timestamp_2 = keyboard_sample_counter;
218
219 // TODO(shinyquagsire23): Figure out what any of these are
220 for (auto& input : mem.unk_input_1) {
221 const u64 last_input_entry = input.header.latest_entry;
222 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
223 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
224
225 input.header.timestamp_ticks = timestamp;
226 input.header.num_entries = input.entries.size();
227 input.header.latest_entry = last_input_entry;
228 input.header.max_entry_index = input.entries.size();
229
230 input.entries[curr_input_entry].timestamp = input_sample_counter;
231 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
232 }
233
234 for (auto& input : mem.unk_input_2) {
235 input.header.timestamp_ticks = timestamp;
236 input.header.num_entries = 17;
237 input.header.latest_entry = 0;
238 input.header.max_entry_index = 0;
239 }
240
241 UnkInput3& input = mem.unk_input_3;
242 const u64 last_input_entry = input.header.latest_entry;
243 const u64 curr_input_entry = (input.header.latest_entry + 1) % input.entries.size();
244 const u64 input_sample_counter = input.entries[last_input_entry].timestamp + 1;
245
246 input.header.timestamp_ticks = timestamp;
247 input.header.num_entries = input.entries.size();
248 input.header.latest_entry = last_input_entry;
249 input.header.max_entry_index = input.entries.size();
250
251 input.entries[curr_input_entry].timestamp = input_sample_counter;
252 input.entries[curr_input_entry].timestamp_2 = input_sample_counter;
253
254 // TODO(shinyquagsire23): Signal events
255
256 std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
257
258 // Reschedule recurrent event
259 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); 140 CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
260 } 141 }
261 142
@@ -265,11 +146,8 @@ private:
265 // CoreTiming update events 146 // CoreTiming update events
266 CoreTiming::EventType* pad_update_event; 147 CoreTiming::EventType* pad_update_event;
267 148
268 // Stored input state info 149 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
269 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 150 controllers{};
270 buttons;
271 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
272 std::unique_ptr<Input::TouchDevice> touch_device;
273}; 151};
274 152
275class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 153class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -299,9 +177,10 @@ public:
299 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, 177 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
300 {21, &Hid::ActivateMouse, "ActivateMouse"}, 178 {21, &Hid::ActivateMouse, "ActivateMouse"},
301 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, 179 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
180 {32, nullptr, "SendKeyboardLockKeyEvent"},
302 {40, nullptr, "AcquireXpadIdEventHandle"}, 181 {40, nullptr, "AcquireXpadIdEventHandle"},
303 {41, nullptr, "ReleaseXpadIdEventHandle"}, 182 {41, nullptr, "ReleaseXpadIdEventHandle"},
304 {51, nullptr, "ActivateXpad"}, 183 {51, &Hid::ActivateXpad, "ActivateXpad"},
305 {55, nullptr, "GetXpadIds"}, 184 {55, nullptr, "GetXpadIds"},
306 {56, nullptr, "ActivateJoyXpad"}, 185 {56, nullptr, "ActivateJoyXpad"},
307 {58, nullptr, "GetJoyXpadLifoHandle"}, 186 {58, nullptr, "GetJoyXpadLifoHandle"},
@@ -329,6 +208,7 @@ public:
329 {80, nullptr, "GetGyroscopeZeroDriftMode"}, 208 {80, nullptr, "GetGyroscopeZeroDriftMode"},
330 {81, nullptr, "ResetGyroscopeZeroDriftMode"}, 209 {81, nullptr, "ResetGyroscopeZeroDriftMode"},
331 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 210 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
211 {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
332 {91, &Hid::ActivateGesture, "ActivateGesture"}, 212 {91, &Hid::ActivateGesture, "ActivateGesture"},
333 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 213 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
334 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 214 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -362,8 +242,8 @@ public:
362 {206, &Hid::SendVibrationValues, "SendVibrationValues"}, 242 {206, &Hid::SendVibrationValues, "SendVibrationValues"},
363 {207, nullptr, "SendVibrationGcErmCommand"}, 243 {207, nullptr, "SendVibrationGcErmCommand"},
364 {208, nullptr, "GetActualVibrationGcErmCommand"}, 244 {208, nullptr, "GetActualVibrationGcErmCommand"},
365 {209, nullptr, "BeginPermitVibrationSession"}, 245 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
366 {210, nullptr, "EndPermitVibrationSession"}, 246 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
367 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, 247 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
368 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, 248 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
369 {302, nullptr, "StopConsoleSixAxisSensor"}, 249 {302, nullptr, "StopConsoleSixAxisSensor"},
@@ -374,6 +254,7 @@ public:
374 {307, nullptr, "FinalizeSevenSixAxisSensor"}, 254 {307, nullptr, "FinalizeSevenSixAxisSensor"},
375 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, 255 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
376 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, 256 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
257 {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
377 {400, nullptr, "IsUsbFullKeyControllerEnabled"}, 258 {400, nullptr, "IsUsbFullKeyControllerEnabled"},
378 {401, nullptr, "EnableUsbFullKeyController"}, 259 {401, nullptr, "EnableUsbFullKeyController"},
379 {402, nullptr, "IsUsbFullKeyControllerConnected"}, 260 {402, nullptr, "IsUsbFullKeyControllerConnected"},
@@ -389,28 +270,35 @@ public:
389 {505, nullptr, "SetPalmaFrModeType"}, 270 {505, nullptr, "SetPalmaFrModeType"},
390 {506, nullptr, "ReadPalmaStep"}, 271 {506, nullptr, "ReadPalmaStep"},
391 {507, nullptr, "EnablePalmaStep"}, 272 {507, nullptr, "EnablePalmaStep"},
392 {508, nullptr, "SuspendPalmaStep"}, 273 {508, nullptr, "ResetPalmaStep"},
393 {509, nullptr, "ResetPalmaStep"}, 274 {509, nullptr, "ReadPalmaApplicationSection"},
394 {510, nullptr, "ReadPalmaApplicationSection"}, 275 {510, nullptr, "WritePalmaApplicationSection"},
395 {511, nullptr, "WritePalmaApplicationSection"}, 276 {511, nullptr, "ReadPalmaUniqueCode"},
396 {512, nullptr, "ReadPalmaUniqueCode"}, 277 {512, nullptr, "SetPalmaUniqueCodeInvalid"},
397 {513, nullptr, "SetPalmaUniqueCodeInvalid"}, 278 {513, nullptr, "WritePalmaActivityEntry"},
279 {514, nullptr, "WritePalmaRgbLedPatternEntry"},
280 {515, nullptr, "WritePalmaWaveEntry"},
281 {516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
282 {517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
283 {518, nullptr, "SuspendPalmaFeature"},
284 {519, nullptr, "GetPalmaOperationResult"},
285 {520, nullptr, "ReadPalmaPlayLog"},
286 {521, nullptr, "ResetPalmaPlayLog"},
287 {522, nullptr, "SetIsPalmaAllConnectable"},
288 {523, nullptr, "SetIsPalmaPairedConnectable"},
289 {524, nullptr, "PairPalma"},
290 {525, nullptr, "SetPalmaBoostMode"},
398 {1000, nullptr, "SetNpadCommunicationMode"}, 291 {1000, nullptr, "SetNpadCommunicationMode"},
399 {1001, nullptr, "GetNpadCommunicationMode"}, 292 {1001, nullptr, "GetNpadCommunicationMode"},
400 }; 293 };
401 // clang-format on 294 // clang-format on
402 295
403 RegisterHandlers(functions); 296 RegisterHandlers(functions);
404
405 auto& kernel = Core::System::GetInstance().Kernel();
406 event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "hid:EventHandle");
407 } 297 }
408 ~Hid() = default; 298 ~Hid() = default;
409 299
410private: 300private:
411 std::shared_ptr<IAppletResource> applet_resource; 301 std::shared_ptr<IAppletResource> applet_resource;
412 u32 joy_hold_type{0};
413 Kernel::SharedPtr<Kernel::Event> event;
414 302
415 void CreateAppletResource(Kernel::HLERequestContext& ctx) { 303 void CreateAppletResource(Kernel::HLERequestContext& ctx) {
416 if (applet_resource == nullptr) { 304 if (applet_resource == nullptr) {
@@ -423,31 +311,59 @@ private:
423 LOG_DEBUG(Service_HID, "called"); 311 LOG_DEBUG(Service_HID, "called");
424 } 312 }
425 313
314 void ActivateXpad(Kernel::HLERequestContext& ctx) {
315 applet_resource->ActivateController(HidController::XPad);
316 IPC::ResponseBuilder rb{ctx, 2};
317 rb.Push(RESULT_SUCCESS);
318 LOG_DEBUG(Service_HID, "called");
319 }
320
426 void ActivateDebugPad(Kernel::HLERequestContext& ctx) { 321 void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
322 applet_resource->ActivateController(HidController::DebugPad);
427 IPC::ResponseBuilder rb{ctx, 2}; 323 IPC::ResponseBuilder rb{ctx, 2};
428 rb.Push(RESULT_SUCCESS); 324 rb.Push(RESULT_SUCCESS);
429 LOG_WARNING(Service_HID, "(STUBBED) called"); 325 LOG_DEBUG(Service_HID, "called");
430 } 326 }
431 327
432 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { 328 void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
329 applet_resource->ActivateController(HidController::Touchscreen);
433 IPC::ResponseBuilder rb{ctx, 2}; 330 IPC::ResponseBuilder rb{ctx, 2};
434 rb.Push(RESULT_SUCCESS); 331 rb.Push(RESULT_SUCCESS);
435 LOG_WARNING(Service_HID, "(STUBBED) called"); 332 LOG_DEBUG(Service_HID, "called");
436 } 333 }
437 334
438 void ActivateMouse(Kernel::HLERequestContext& ctx) { 335 void ActivateMouse(Kernel::HLERequestContext& ctx) {
336 applet_resource->ActivateController(HidController::Mouse);
439 IPC::ResponseBuilder rb{ctx, 2}; 337 IPC::ResponseBuilder rb{ctx, 2};
440 rb.Push(RESULT_SUCCESS); 338 rb.Push(RESULT_SUCCESS);
441 LOG_WARNING(Service_HID, "(STUBBED) called"); 339 LOG_DEBUG(Service_HID, "called");
442 } 340 }
443 341
444 void ActivateKeyboard(Kernel::HLERequestContext& ctx) { 342 void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
343 applet_resource->ActivateController(HidController::Keyboard);
445 IPC::ResponseBuilder rb{ctx, 2}; 344 IPC::ResponseBuilder rb{ctx, 2};
446 rb.Push(RESULT_SUCCESS); 345 rb.Push(RESULT_SUCCESS);
447 LOG_WARNING(Service_HID, "(STUBBED) called"); 346 LOG_DEBUG(Service_HID, "called");
347 }
348
349 void ActivateGesture(Kernel::HLERequestContext& ctx) {
350 applet_resource->ActivateController(HidController::Gesture);
351 IPC::ResponseBuilder rb{ctx, 2};
352 rb.Push(RESULT_SUCCESS);
353 LOG_DEBUG(Service_HID, "called");
354 }
355
356 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
357 // Should have no effect with how our npad sets up the data
358 applet_resource->ActivateController(HidController::NPad);
359 IPC::ResponseBuilder rb{ctx, 2};
360 rb.Push(RESULT_SUCCESS);
361 LOG_DEBUG(Service_HID, "called");
448 } 362 }
449 363
450 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { 364 void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
365 IPC::RequestParser rp{ctx};
366 auto handle = rp.PopRaw<u32>();
451 IPC::ResponseBuilder rb{ctx, 2}; 367 IPC::ResponseBuilder rb{ctx, 2};
452 rb.Push(RESULT_SUCCESS); 368 rb.Push(RESULT_SUCCESS);
453 LOG_WARNING(Service_HID, "(STUBBED) called"); 369 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -468,84 +384,168 @@ private:
468 } 384 }
469 385
470 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 386 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
387 IPC::RequestParser rp{ctx};
388 auto supported_styleset = rp.PopRaw<u32>();
389 applet_resource->GetController<Controller_NPad>(HidController::NPad)
390 .SetSupportedStyleSet({supported_styleset});
391
471 IPC::ResponseBuilder rb{ctx, 2}; 392 IPC::ResponseBuilder rb{ctx, 2};
472 rb.Push(RESULT_SUCCESS); 393 rb.Push(RESULT_SUCCESS);
473 LOG_WARNING(Service_HID, "(STUBBED) called"); 394
395 LOG_DEBUG(Service_HID, "called");
474 } 396 }
475 397
476 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 398 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
399 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
400
477 IPC::ResponseBuilder rb{ctx, 3}; 401 IPC::ResponseBuilder rb{ctx, 3};
478 rb.Push(RESULT_SUCCESS); 402 rb.Push(RESULT_SUCCESS);
479 rb.Push<u32>(0); 403 rb.Push<u32>(controller.GetSupportedStyleSet().raw);
480 LOG_WARNING(Service_HID, "(STUBBED) called"); 404 LOG_DEBUG(Service_HID, "called");
481 } 405 }
482 406
483 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { 407 void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
408 applet_resource->GetController<Controller_NPad>(HidController::NPad)
409 .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
484 IPC::ResponseBuilder rb{ctx, 2}; 410 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(RESULT_SUCCESS); 411 rb.Push(RESULT_SUCCESS);
486 LOG_WARNING(Service_HID, "(STUBBED) called"); 412 LOG_DEBUG(Service_HID, "called");
487 } 413 }
488 414
489 void ActivateNpad(Kernel::HLERequestContext& ctx) { 415 void ActivateNpad(Kernel::HLERequestContext& ctx) {
490 IPC::ResponseBuilder rb{ctx, 2}; 416 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(RESULT_SUCCESS); 417 rb.Push(RESULT_SUCCESS);
492 LOG_WARNING(Service_HID, "(STUBBED) called"); 418 applet_resource->ActivateController(HidController::NPad);
419 LOG_DEBUG(Service_HID, "called");
493 } 420 }
494 421
495 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { 422 void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
423 IPC::RequestParser rp{ctx};
424 auto npad_id = rp.PopRaw<u32>();
496 IPC::ResponseBuilder rb{ctx, 2, 1}; 425 IPC::ResponseBuilder rb{ctx, 2, 1};
497 rb.Push(RESULT_SUCCESS); 426 rb.Push(RESULT_SUCCESS);
498 rb.PushCopyObjects(event); 427 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
499 LOG_WARNING(Service_HID, "(STUBBED) called"); 428 .GetStyleSetChangedEvent());
429 LOG_DEBUG(Service_HID, "called");
500 } 430 }
501 431
502 void DisconnectNpad(Kernel::HLERequestContext& ctx) { 432 void DisconnectNpad(Kernel::HLERequestContext& ctx) {
433 IPC::RequestParser rp{ctx};
434 auto npad_id = rp.PopRaw<u32>();
435 applet_resource->GetController<Controller_NPad>(HidController::NPad)
436 .DisconnectNPad(npad_id);
503 IPC::ResponseBuilder rb{ctx, 2}; 437 IPC::ResponseBuilder rb{ctx, 2};
504 rb.Push(RESULT_SUCCESS); 438 rb.Push(RESULT_SUCCESS);
505 LOG_WARNING(Service_HID, "(STUBBED) called"); 439 LOG_DEBUG(Service_HID, "called");
506 } 440 }
507 441
508 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 442 void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
509 IPC::ResponseBuilder rb{ctx, 2}; 443 IPC::RequestParser rp{ctx};
444 auto npad_id = rp.PopRaw<u32>();
445 IPC::ResponseBuilder rb{ctx, 4};
510 rb.Push(RESULT_SUCCESS); 446 rb.Push(RESULT_SUCCESS);
511 LOG_WARNING(Service_HID, "(STUBBED) called"); 447 rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
448 .GetLedPattern(npad_id)
449 .raw);
450 LOG_DEBUG(Service_HID, "called");
512 } 451 }
513 452
514 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 453 void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
454 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
455 IPC::RequestParser rp{ctx};
456 const auto hold_type = rp.PopRaw<u64>();
457 controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
458
515 IPC::ResponseBuilder rb{ctx, 2}; 459 IPC::ResponseBuilder rb{ctx, 2};
516 rb.Push(RESULT_SUCCESS); 460 rb.Push(RESULT_SUCCESS);
517 LOG_WARNING(Service_HID, "(STUBBED) called"); 461 LOG_DEBUG(Service_HID, "called");
518 } 462 }
519 463
520 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { 464 void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
521 IPC::ResponseBuilder rb{ctx, 3}; 465 const auto& controller =
466 applet_resource->GetController<Controller_NPad>(HidController::NPad);
467 IPC::ResponseBuilder rb{ctx, 4};
522 rb.Push(RESULT_SUCCESS); 468 rb.Push(RESULT_SUCCESS);
523 rb.Push(joy_hold_type); 469 rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
524 LOG_WARNING(Service_HID, "(STUBBED) called"); 470 LOG_DEBUG(Service_HID, "called");
525 } 471 }
526 472
527 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { 473 void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
474 IPC::RequestParser rp{ctx};
475 auto npad_id = rp.PopRaw<u32>();
528 IPC::ResponseBuilder rb{ctx, 2}; 476 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(RESULT_SUCCESS); 477 rb.Push(RESULT_SUCCESS);
530 LOG_WARNING(Service_HID, "(STUBBED) called"); 478 LOG_WARNING(Service_HID, "(STUBBED) called");
531 } 479 }
532 480
481 void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
482 applet_resource->GetController<Controller_NPad>(HidController::NPad)
483 .SetVibrationEnabled(true);
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(RESULT_SUCCESS);
486 LOG_DEBUG(Service_HID, "called");
487 }
488
489 void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
490 applet_resource->GetController<Controller_NPad>(HidController::NPad)
491 .SetVibrationEnabled(false);
492 IPC::ResponseBuilder rb{ctx, 2};
493 rb.Push(RESULT_SUCCESS);
494 LOG_DEBUG(Service_HID, "called");
495 }
496
533 void SendVibrationValue(Kernel::HLERequestContext& ctx) { 497 void SendVibrationValue(Kernel::HLERequestContext& ctx) {
498 IPC::RequestParser rp{ctx};
499 const auto controller_id = rp.PopRaw<u32>();
500 const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
501
534 IPC::ResponseBuilder rb{ctx, 2}; 502 IPC::ResponseBuilder rb{ctx, 2};
535 rb.Push(RESULT_SUCCESS); 503 rb.Push(RESULT_SUCCESS);
536 LOG_WARNING(Service_HID, "(STUBBED) called"); 504
505 applet_resource->GetController<Controller_NPad>(HidController::NPad)
506 .VibrateController({controller_id}, {vibration_values});
507 LOG_DEBUG(Service_HID, "called");
537 } 508 }
538 509
539 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { 510 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
511 const auto controllers = ctx.ReadBuffer(0);
512 const auto vibrations = ctx.ReadBuffer(1);
513
514 std::vector<u32> controller_list(controllers.size() / sizeof(u32));
515 std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
516 sizeof(Controller_NPad::Vibration));
517
518 std::memcpy(controller_list.data(), controllers.data(), controllers.size());
519 std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
520 std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
521 [](u32 controller_id) { return controller_id - 3; });
522
523 applet_resource->GetController<Controller_NPad>(HidController::NPad)
524 .VibrateController(controller_list, vibration_list);
525
540 IPC::ResponseBuilder rb{ctx, 2}; 526 IPC::ResponseBuilder rb{ctx, 2};
541 rb.Push(RESULT_SUCCESS); 527 rb.Push(RESULT_SUCCESS);
542 LOG_WARNING(Service_HID, "(STUBBED) called"); 528 LOG_DEBUG(Service_HID, "called");
529 }
530
531 void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
532 IPC::ResponseBuilder rb{ctx, 6};
533 rb.Push(RESULT_SUCCESS);
534 rb.PushRaw<Controller_NPad::Vibration>(
535 applet_resource->GetController<Controller_NPad>(HidController::NPad)
536 .GetLastVibration());
537 LOG_DEBUG(Service_HID, "called");
543 } 538 }
544 539
545 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 540 void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
541 IPC::RequestParser rp{ctx};
542 const auto npad_id = rp.PopRaw<u32>();
543 auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
544 controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
545
546 IPC::ResponseBuilder rb{ctx, 2}; 546 IPC::ResponseBuilder rb{ctx, 2};
547 rb.Push(RESULT_SUCCESS); 547 rb.Push(RESULT_SUCCESS);
548 LOG_WARNING(Service_HID, "(STUBBED) called"); 548 LOG_DEBUG(Service_HID, "called");
549 } 549 }
550 550
551 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 551 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -555,6 +555,8 @@ private:
555 } 555 }
556 556
557 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { 557 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
558 IPC::RequestParser rp{ctx};
559 auto mode = rp.PopRaw<u32>();
558 IPC::ResponseBuilder rb{ctx, 2}; 560 IPC::ResponseBuilder rb{ctx, 2};
559 rb.Push(RESULT_SUCCESS); 561 rb.Push(RESULT_SUCCESS);
560 LOG_WARNING(Service_HID, "(STUBBED) called"); 562 LOG_WARNING(Service_HID, "(STUBBED) called");
@@ -563,8 +565,9 @@ private:
563 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { 565 void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
564 IPC::ResponseBuilder rb{ctx, 4}; 566 IPC::ResponseBuilder rb{ctx, 4};
565 rb.Push(RESULT_SUCCESS); 567 rb.Push(RESULT_SUCCESS);
566 rb.Push<u64>(0); 568 rb.Push<u32>(1);
567 LOG_WARNING(Service_HID, "(STUBBED) called"); 569 rb.Push<u32>(0);
570 LOG_DEBUG(Service_HID, "called");
568 } 571 }
569 572
570 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { 573 void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
@@ -574,12 +577,6 @@ private:
574 LOG_DEBUG(Service_HID, "called"); 577 LOG_DEBUG(Service_HID, "called");
575 } 578 }
576 579
577 void SendVibrationValues(Kernel::HLERequestContext& ctx) {
578 IPC::ResponseBuilder rb{ctx, 2};
579 rb.Push(RESULT_SUCCESS);
580 LOG_WARNING(Service_HID, "(STUBBED) called");
581 }
582
583 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { 580 void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
584 IPC::ResponseBuilder rb{ctx, 2}; 581 IPC::ResponseBuilder rb{ctx, 2};
585 rb.Push(RESULT_SUCCESS); 582 rb.Push(RESULT_SUCCESS);
@@ -597,18 +594,6 @@ private:
597 rb.Push(RESULT_SUCCESS); 594 rb.Push(RESULT_SUCCESS);
598 LOG_WARNING(Service_HID, "(STUBBED) called"); 595 LOG_WARNING(Service_HID, "(STUBBED) called");
599 } 596 }
600
601 void ActivateGesture(Kernel::HLERequestContext& ctx) {
602 IPC::ResponseBuilder rb{ctx, 2};
603 rb.Push(RESULT_SUCCESS);
604 LOG_WARNING(Service_HID, "(STUBBED) called");
605 }
606
607 void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
608 IPC::ResponseBuilder rb{ctx, 2};
609 rb.Push(RESULT_SUCCESS);
610 LOG_WARNING(Service_HID, "(STUBBED) called");
611 }
612}; 597};
613 598
614class HidDbg final : public ServiceFramework<HidDbg> { 599class HidDbg final : public ServiceFramework<HidDbg> {
@@ -650,6 +635,7 @@ public:
650 {140, nullptr, "DeactivateConsoleSixAxisSensor"}, 635 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
651 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, 636 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
652 {142, nullptr, "DeactivateSevenSixAxisSensor"}, 637 {142, nullptr, "DeactivateSevenSixAxisSensor"},
638 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
653 {201, nullptr, "ActivateFirmwareUpdate"}, 639 {201, nullptr, "ActivateFirmwareUpdate"},
654 {202, nullptr, "DeactivateFirmwareUpdate"}, 640 {202, nullptr, "DeactivateFirmwareUpdate"},
655 {203, nullptr, "StartFirmwareUpdate"}, 641 {203, nullptr, "StartFirmwareUpdate"},
@@ -660,12 +646,23 @@ public:
660 {208, nullptr, "StartFirmwareUpdateForRevert"}, 646 {208, nullptr, "StartFirmwareUpdateForRevert"},
661 {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, 647 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
662 {210, nullptr, "IsFirmwareUpdatingDevice"}, 648 {210, nullptr, "IsFirmwareUpdatingDevice"},
649 {211, nullptr, "StartFirmwareUpdateIndividual"},
650 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
651 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
663 {221, nullptr, "UpdateControllerColor"}, 652 {221, nullptr, "UpdateControllerColor"},
664 {222, nullptr, "ConnectUsbPadsAsync"}, 653 {222, nullptr, "ConnectUsbPadsAsync"},
665 {223, nullptr, "DisconnectUsbPadsAsync"}, 654 {223, nullptr, "DisconnectUsbPadsAsync"},
666 {224, nullptr, "UpdateDesignInfo"}, 655 {224, nullptr, "UpdateDesignInfo"},
667 {225, nullptr, "GetUniquePadDriverState"}, 656 {225, nullptr, "GetUniquePadDriverState"},
668 {226, nullptr, "GetSixAxisSensorDriverStates"}, 657 {226, nullptr, "GetSixAxisSensorDriverStates"},
658 {227, nullptr, "GetRxPacketHistory"},
659 {228, nullptr, "AcquireOperationEventHandle"},
660 {229, nullptr, "ReadSerialFlash"},
661 {230, nullptr, "WriteSerialFlash"},
662 {231, nullptr, "GetOperationResult"},
663 {232, nullptr, "EnableShipmentMode"},
664 {233, nullptr, "ClearPairingInfo"},
665 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
669 {301, nullptr, "GetAbstractedPadHandles"}, 666 {301, nullptr, "GetAbstractedPadHandles"},
670 {302, nullptr, "GetAbstractedPadState"}, 667 {302, nullptr, "GetAbstractedPadState"},
671 {303, nullptr, "GetAbstractedPadsState"}, 668 {303, nullptr, "GetAbstractedPadsState"},
@@ -673,6 +670,8 @@ public:
673 {322, nullptr, "UnsetAutoPilotVirtualPadState"}, 670 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
674 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, 671 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
675 {350, nullptr, "AddRegisteredDevice"}, 672 {350, nullptr, "AddRegisteredDevice"},
673 {400, nullptr, "DisableExternalMcuOnNxDevice"},
674 {401, nullptr, "DisableRailDeviceFiltering"},
676 }; 675 };
677 // clang-format on 676 // clang-format on
678 677
@@ -708,7 +707,9 @@ public:
708 {307, nullptr, "GetNpadSystemExtStyle"}, 707 {307, nullptr, "GetNpadSystemExtStyle"},
709 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, 708 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
710 {309, nullptr, "GetNpadFullKeyGripColor"}, 709 {309, nullptr, "GetNpadFullKeyGripColor"},
710 {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
711 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, 711 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
712 {312, nullptr, "SetSupportedNpadStyleSetAll"},
712 {321, nullptr, "GetUniquePadsFromNpad"}, 713 {321, nullptr, "GetUniquePadsFromNpad"},
713 {322, nullptr, "GetIrSensorState"}, 714 {322, nullptr, "GetIrSensorState"},
714 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, 715 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
@@ -733,6 +734,7 @@ public:
733 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, 734 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
734 {547, nullptr, "GetAllowedBluetoothLinksCount"}, 735 {547, nullptr, "GetAllowedBluetoothLinksCount"},
735 {548, nullptr, "GetRegisteredDevices"}, 736 {548, nullptr, "GetRegisteredDevices"},
737 {549, nullptr, "GetConnectableRegisteredDevices"},
736 {700, nullptr, "ActivateUniquePad"}, 738 {700, nullptr, "ActivateUniquePad"},
737 {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, 739 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
738 {703, nullptr, "GetUniquePadIds"}, 740 {703, nullptr, "GetUniquePadIds"},
@@ -761,6 +763,7 @@ public:
761 {850, nullptr, "IsUsbFullKeyControllerEnabled"}, 763 {850, nullptr, "IsUsbFullKeyControllerEnabled"},
762 {851, nullptr, "EnableUsbFullKeyController"}, 764 {851, nullptr, "EnableUsbFullKeyController"},
763 {852, nullptr, "IsUsbConnected"}, 765 {852, nullptr, "IsUsbConnected"},
766 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
764 {900, nullptr, "ActivateInputDetector"}, 767 {900, nullptr, "ActivateInputDetector"},
765 {901, nullptr, "NotifyInputDetector"}, 768 {901, nullptr, "NotifyInputDetector"},
766 {1000, nullptr, "InitializeFirmwareUpdate"}, 769 {1000, nullptr, "InitializeFirmwareUpdate"},
@@ -780,6 +783,12 @@ public:
780 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, 783 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
781 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, 784 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
782 {1100, nullptr, "GetHidbusSystemServiceObject"}, 785 {1100, nullptr, "GetHidbusSystemServiceObject"},
786 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
787 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
788 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
789 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
790 {1133, nullptr, "StartUsbFirmwareUpdate"},
791 {1134, nullptr, "GetUsbFirmwareUpdateState"},
783 }; 792 };
784 // clang-format on 793 // clang-format on
785 794
@@ -818,6 +827,7 @@ public:
818 {11, nullptr, "EnableJoyPollingReceiveMode"}, 827 {11, nullptr, "EnableJoyPollingReceiveMode"},
819 {12, nullptr, "DisableJoyPollingReceiveMode"}, 828 {12, nullptr, "DisableJoyPollingReceiveMode"},
820 {13, nullptr, "GetPollingData"}, 829 {13, nullptr, "GetPollingData"},
830 {14, nullptr, "SetStatusManagerType"},
821 }; 831 };
822 // clang-format on 832 // clang-format on
823 833
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 88d926808..773035460 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,408 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7namespace SM {
8#include "common/bit_field.h" 8class ServiceManager;
9#include "common/common_types.h" 9}
10#include "core/hle/service/service.h"
11 10
12namespace Service::HID { 11namespace Service::HID {
13 12
14// Begin enums and output structs
15
16constexpr u32 HID_NUM_ENTRIES = 17;
17constexpr u32 HID_NUM_LAYOUTS = 7;
18constexpr s32 HID_JOYSTICK_MAX = 0x8000;
19constexpr s32 HID_JOYSTICK_MIN = -0x8000;
20
21constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
22constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
23constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
24constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
25
26enum ControllerType : u32 {
27 ControllerType_ProController = 1 << 0,
28 ControllerType_Handheld = 1 << 1,
29 ControllerType_JoyconPair = 1 << 2,
30 ControllerType_JoyconLeft = 1 << 3,
31 ControllerType_JoyconRight = 1 << 4,
32};
33
34enum ControllerLayoutType : u32 {
35 Layout_ProController = 0, // Pro Controller or HID gamepad
36 Layout_Handheld = 1, // Two Joy-Con docked to rails
37 Layout_Single = 2, // Horizontal single Joy-Con or pair of Joy-Con, adjusted for orientation
38 Layout_Left = 3, // Only raw left Joy-Con state, no orientation adjustment
39 Layout_Right = 4, // Only raw right Joy-Con state, no orientation adjustment
40 Layout_DefaultDigital = 5, // Same as next, but sticks have 8-direction values only
41 Layout_Default = 6, // Safe default, single Joy-Con have buttons/sticks rotated for orientation
42};
43
44enum ControllerColorDescription {
45 ColorDesc_ColorsNonexistent = 1 << 1,
46};
47
48enum ControllerConnectionState {
49 ConnectionState_Connected = 1 << 0,
50 ConnectionState_Wired = 1 << 1,
51};
52
53enum ControllerJoystick {
54 Joystick_Left = 0,
55 Joystick_Right = 1,
56};
57
58enum ControllerID {
59 Controller_Player1 = 0,
60 Controller_Player2 = 1,
61 Controller_Player3 = 2,
62 Controller_Player4 = 3,
63 Controller_Player5 = 4,
64 Controller_Player6 = 5,
65 Controller_Player7 = 6,
66 Controller_Player8 = 7,
67 Controller_Handheld = 8,
68 Controller_Unknown = 9,
69};
70
71// End enums and output structs
72
73// Begin UnkInput3
74
75struct UnkInput3Header {
76 u64 timestamp_ticks;
77 u64 num_entries;
78 u64 latest_entry;
79 u64 max_entry_index;
80};
81static_assert(sizeof(UnkInput3Header) == 0x20, "HID UnkInput3 header structure has incorrect size");
82
83struct UnkInput3Entry {
84 u64 timestamp;
85 u64 timestamp_2;
86 u64 unk_8;
87 u64 unk_10;
88 u64 unk_18;
89};
90static_assert(sizeof(UnkInput3Entry) == 0x28, "HID UnkInput3 entry structure has incorrect size");
91
92struct UnkInput3 {
93 UnkInput3Header header;
94 std::array<UnkInput3Entry, 17> entries;
95 std::array<u8, 0x138> padding;
96};
97static_assert(sizeof(UnkInput3) == 0x400, "HID UnkInput3 structure has incorrect size");
98
99// End UnkInput3
100
101// Begin TouchScreen
102
103struct TouchScreenHeader {
104 u64 timestamp_ticks;
105 u64 num_entries;
106 u64 latest_entry;
107 u64 max_entry_index;
108 u64 timestamp;
109};
110static_assert(sizeof(TouchScreenHeader) == 0x28,
111 "HID touch screen header structure has incorrect size");
112
113struct TouchScreenEntryHeader {
114 u64 timestamp;
115 u64 num_touches;
116};
117static_assert(sizeof(TouchScreenEntryHeader) == 0x10,
118 "HID touch screen entry header structure has incorrect size");
119
120struct TouchScreenEntryTouch {
121 u64 timestamp;
122 u32 padding;
123 u32 touch_index;
124 u32 x;
125 u32 y;
126 u32 diameter_x;
127 u32 diameter_y;
128 u32 angle;
129 u32 padding_2;
130};
131static_assert(sizeof(TouchScreenEntryTouch) == 0x28,
132 "HID touch screen touch structure has incorrect size");
133
134struct TouchScreenEntry {
135 TouchScreenEntryHeader header;
136 std::array<TouchScreenEntryTouch, 16> touches;
137 u64 unk;
138};
139static_assert(sizeof(TouchScreenEntry) == 0x298,
140 "HID touch screen entry structure has incorrect size");
141
142struct TouchScreen {
143 TouchScreenHeader header;
144 std::array<TouchScreenEntry, 17> entries;
145 std::array<u8, 0x3c0> padding;
146};
147static_assert(sizeof(TouchScreen) == 0x3000, "HID touch screen structure has incorrect size");
148
149// End TouchScreen
150
151// Begin Mouse
152
153struct MouseHeader {
154 u64 timestamp_ticks;
155 u64 num_entries;
156 u64 latest_entry;
157 u64 max_entry_index;
158};
159static_assert(sizeof(MouseHeader) == 0x20, "HID mouse header structure has incorrect size");
160
161struct MouseButtonState {
162 union {
163 u64 hex{};
164
165 // Buttons
166 BitField<0, 1, u64> left;
167 BitField<1, 1, u64> right;
168 BitField<2, 1, u64> middle;
169 BitField<3, 1, u64> forward;
170 BitField<4, 1, u64> back;
171 };
172};
173
174struct MouseEntry {
175 u64 timestamp;
176 u64 timestamp_2;
177 u32 x;
178 u32 y;
179 u32 velocity_x;
180 u32 velocity_y;
181 u32 scroll_velocity_x;
182 u32 scroll_velocity_y;
183 MouseButtonState buttons;
184};
185static_assert(sizeof(MouseEntry) == 0x30, "HID mouse entry structure has incorrect size");
186
187struct Mouse {
188 MouseHeader header;
189 std::array<MouseEntry, 17> entries;
190 std::array<u8, 0xB0> padding;
191};
192static_assert(sizeof(Mouse) == 0x400, "HID mouse structure has incorrect size");
193
194// End Mouse
195
196// Begin Keyboard
197
198struct KeyboardHeader {
199 u64 timestamp_ticks;
200 u64 num_entries;
201 u64 latest_entry;
202 u64 max_entry_index;
203};
204static_assert(sizeof(KeyboardHeader) == 0x20, "HID keyboard header structure has incorrect size");
205
206struct KeyboardModifierKeyState {
207 union {
208 u64 hex{};
209
210 // Buttons
211 BitField<0, 1, u64> lctrl;
212 BitField<1, 1, u64> lshift;
213 BitField<2, 1, u64> lalt;
214 BitField<3, 1, u64> lmeta;
215 BitField<4, 1, u64> rctrl;
216 BitField<5, 1, u64> rshift;
217 BitField<6, 1, u64> ralt;
218 BitField<7, 1, u64> rmeta;
219 BitField<8, 1, u64> capslock;
220 BitField<9, 1, u64> scrolllock;
221 BitField<10, 1, u64> numlock;
222 };
223};
224
225struct KeyboardEntry {
226 u64 timestamp;
227 u64 timestamp_2;
228 KeyboardModifierKeyState modifier;
229 u32 keys[8];
230};
231static_assert(sizeof(KeyboardEntry) == 0x38, "HID keyboard entry structure has incorrect size");
232
233struct Keyboard {
234 KeyboardHeader header;
235 std::array<KeyboardEntry, 17> entries;
236 std::array<u8, 0x28> padding;
237};
238static_assert(sizeof(Keyboard) == 0x400, "HID keyboard structure has incorrect size");
239
240// End Keyboard
241
242// Begin UnkInput1
243
244struct UnkInput1Header {
245 u64 timestamp_ticks;
246 u64 num_entries;
247 u64 latest_entry;
248 u64 max_entry_index;
249};
250static_assert(sizeof(UnkInput1Header) == 0x20, "HID UnkInput1 header structure has incorrect size");
251
252struct UnkInput1Entry {
253 u64 timestamp;
254 u64 timestamp_2;
255 u64 unk_8;
256 u64 unk_10;
257 u64 unk_18;
258};
259static_assert(sizeof(UnkInput1Entry) == 0x28, "HID UnkInput1 entry structure has incorrect size");
260
261struct UnkInput1 {
262 UnkInput1Header header;
263 std::array<UnkInput1Entry, 17> entries;
264 std::array<u8, 0x138> padding;
265};
266static_assert(sizeof(UnkInput1) == 0x400, "HID UnkInput1 structure has incorrect size");
267
268// End UnkInput1
269
270// Begin UnkInput2
271
272struct UnkInput2Header {
273 u64 timestamp_ticks;
274 u64 num_entries;
275 u64 latest_entry;
276 u64 max_entry_index;
277};
278static_assert(sizeof(UnkInput2Header) == 0x20, "HID UnkInput2 header structure has incorrect size");
279
280struct UnkInput2 {
281 UnkInput2Header header;
282 std::array<u8, 0x1E0> padding;
283};
284static_assert(sizeof(UnkInput2) == 0x200, "HID UnkInput2 structure has incorrect size");
285
286// End UnkInput2
287
288// Begin Controller
289
290struct ControllerMAC {
291 u64 timestamp;
292 std::array<u8, 0x8> mac;
293 u64 unk;
294 u64 timestamp_2;
295};
296static_assert(sizeof(ControllerMAC) == 0x20, "HID controller MAC structure has incorrect size");
297
298struct ControllerHeader {
299 u32 type;
300 u32 is_half;
301 u32 single_colors_descriptor;
302 u32 single_color_body;
303 u32 single_color_buttons;
304 u32 split_colors_descriptor;
305 u32 left_color_body;
306 u32 left_color_buttons;
307 u32 right_color_body;
308 u32 right_color_buttons;
309};
310static_assert(sizeof(ControllerHeader) == 0x28,
311 "HID controller header structure has incorrect size");
312
313struct ControllerLayoutHeader {
314 u64 timestamp_ticks;
315 u64 num_entries;
316 u64 latest_entry;
317 u64 max_entry_index;
318};
319static_assert(sizeof(ControllerLayoutHeader) == 0x20,
320 "HID controller layout header structure has incorrect size");
321
322struct ControllerPadState {
323 union {
324 u64 hex{};
325
326 // Buttons
327 BitField<0, 1, u64> a;
328 BitField<1, 1, u64> b;
329 BitField<2, 1, u64> x;
330 BitField<3, 1, u64> y;
331 BitField<4, 1, u64> lstick;
332 BitField<5, 1, u64> rstick;
333 BitField<6, 1, u64> l;
334 BitField<7, 1, u64> r;
335 BitField<8, 1, u64> zl;
336 BitField<9, 1, u64> zr;
337 BitField<10, 1, u64> plus;
338 BitField<11, 1, u64> minus;
339
340 // D-pad buttons
341 BitField<12, 1, u64> dleft;
342 BitField<13, 1, u64> dup;
343 BitField<14, 1, u64> dright;
344 BitField<15, 1, u64> ddown;
345
346 // Left stick directions
347 BitField<16, 1, u64> lstick_left;
348 BitField<17, 1, u64> lstick_up;
349 BitField<18, 1, u64> lstick_right;
350 BitField<19, 1, u64> lstick_down;
351
352 // Right stick directions
353 BitField<20, 1, u64> rstick_left;
354 BitField<21, 1, u64> rstick_up;
355 BitField<22, 1, u64> rstick_right;
356 BitField<23, 1, u64> rstick_down;
357
358 BitField<24, 1, u64> sl;
359 BitField<25, 1, u64> sr;
360 };
361};
362
363struct ControllerInputEntry {
364 u64 timestamp;
365 u64 timestamp_2;
366 ControllerPadState buttons;
367 s32 joystick_left_x;
368 s32 joystick_left_y;
369 s32 joystick_right_x;
370 s32 joystick_right_y;
371 u64 connection_state;
372};
373static_assert(sizeof(ControllerInputEntry) == 0x30,
374 "HID controller input entry structure has incorrect size");
375
376struct ControllerLayout {
377 ControllerLayoutHeader header;
378 std::array<ControllerInputEntry, 17> entries;
379};
380static_assert(sizeof(ControllerLayout) == 0x350,
381 "HID controller layout structure has incorrect size");
382
383struct Controller {
384 ControllerHeader header;
385 std::array<ControllerLayout, HID_NUM_LAYOUTS> layouts;
386 std::array<u8, 0x2a70> unk_1;
387 ControllerMAC mac_left;
388 ControllerMAC mac_right;
389 std::array<u8, 0xdf8> unk_2;
390};
391static_assert(sizeof(Controller) == 0x5000, "HID controller structure has incorrect size");
392
393// End Controller
394
395struct SharedMemory {
396 UnkInput3 unk_input_3;
397 TouchScreen touchscreen;
398 Mouse mouse;
399 Keyboard keyboard;
400 std::array<UnkInput1, 4> unk_input_1;
401 std::array<UnkInput2, 3> unk_input_2;
402 std::array<u8, 0x800> unk_section_8;
403 std::array<u8, 0x4000> controller_serials;
404 std::array<Controller, 10> controllers;
405 std::array<u8, 0x4600> unk_section_9;
406};
407static_assert(sizeof(SharedMemory) == 0x40000, "HID Shared Memory structure has incorrect size");
408
409/// Reload input devices. Used when input configuration changed 13/// Reload input devices. Used when input configuration changed
410void ReloadInputDevices(); 14void ReloadInputDevices();
411 15
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index 7321584e1..164c57e18 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -18,35 +18,35 @@ public:
18 explicit LBL() : ServiceFramework{"lbl"} { 18 explicit LBL() : ServiceFramework{"lbl"} {
19 // clang-format off 19 // clang-format off
20 static const FunctionInfo functions[] = { 20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Unknown1"}, 21 {0, nullptr, "SaveCurrentSetting"},
22 {1, nullptr, "Unknown2"}, 22 {1, nullptr, "LoadCurrentSetting"},
23 {2, nullptr, "Unknown3"}, 23 {2, nullptr, "SetCurrentBrightnessSetting"},
24 {3, nullptr, "GetCurrentBacklightLevel"}, 24 {3, nullptr, "GetCurrentBrightnessSetting"},
25 {4, nullptr, "Unknown4"}, 25 {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
26 {5, nullptr, "GetAlsComputedBacklightLevel"}, 26 {5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
27 {6, nullptr, "TurnOffBacklight"}, 27 {6, nullptr, "SwitchBacklightOn"},
28 {7, nullptr, "TurnOnBacklight"}, 28 {7, nullptr, "SwitchBacklightOff"},
29 {8, nullptr, "GetBacklightStatus"}, 29 {8, nullptr, "GetBacklightSwitchStatus"},
30 {9, nullptr, "Unknown5"}, 30 {9, nullptr, "EnableDimming"},
31 {10, nullptr, "Unknown6"}, 31 {10, nullptr, "DisableDimming"},
32 {11, nullptr, "Unknown7"}, 32 {11, nullptr, "IsDimmingEnabled"},
33 {12, nullptr, "Unknown8"}, 33 {12, nullptr, "EnableAutoBrightnessControl"},
34 {13, nullptr, "Unknown9"}, 34 {13, nullptr, "DisableAutoBrightnessControl"},
35 {14, nullptr, "Unknown10"}, 35 {14, nullptr, "IsAutoBrightnessControlEnabled"},
36 {15, nullptr, "GetAutoBrightnessSetting"}, 36 {15, nullptr, "SetAmbientLightSensorValue"},
37 {16, nullptr, "ReadRawLightSensor"}, 37 {16, nullptr, "GetAmbientLightSensorValue"},
38 {17, nullptr, "Unknown11"}, 38 {17, nullptr, "SetBrightnessReflectionDelayLevel"},
39 {18, nullptr, "Unknown12"}, 39 {18, nullptr, "GetBrightnessReflectionDelayLevel"},
40 {19, nullptr, "Unknown13"}, 40 {19, nullptr, "SetCurrentBrightnessMapping"},
41 {20, nullptr, "Unknown14"}, 41 {20, nullptr, "GetCurrentBrightnessMapping"},
42 {21, nullptr, "Unknown15"}, 42 {21, nullptr, "SetCurrentAmbientLightSensorMapping"},
43 {22, nullptr, "Unknown16"}, 43 {22, nullptr, "GetCurrentAmbientLightSensorMapping"},
44 {23, nullptr, "Unknown17"}, 44 {23, nullptr, "IsAmbientLightSensorAvailable"},
45 {24, nullptr, "Unknown18"}, 45 {24, nullptr, "SetCurrentBrightnessSettingForVrMode"},
46 {25, nullptr, "Unknown19"}, 46 {25, nullptr, "GetCurrentBrightnessSettingForVrMode"},
47 {26, &LBL::EnableVrMode, "EnableVrMode"}, 47 {26, &LBL::EnableVrMode, "EnableVrMode"},
48 {27, &LBL::DisableVrMode, "DisableVrMode"}, 48 {27, &LBL::DisableVrMode, "DisableVrMode"},
49 {28, &LBL::GetVrMode, "GetVrMode"}, 49 {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
50 }; 50 };
51 // clang-format on 51 // clang-format on
52 52
@@ -72,7 +72,7 @@ private:
72 LOG_DEBUG(Service_LBL, "called"); 72 LOG_DEBUG(Service_LBL, "called");
73 } 73 }
74 74
75 void GetVrMode(Kernel::HLERequestContext& ctx) { 75 void IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
76 IPC::ResponseBuilder rb{ctx, 3}; 76 IPC::ResponseBuilder rb{ctx, 3};
77 rb.Push(RESULT_SUCCESS); 77 rb.Push(RESULT_SUCCESS);
78 rb.Push(vr_mode_enabled); 78 rb.Push(vr_mode_enabled);
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index ec32faf15..d607d985e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -3,9 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory> 5#include <memory>
6#include <fmt/format.h>
6 7
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/process.h"
7#include "core/hle/service/ldr/ldr.h" 10#include "core/hle/service/ldr/ldr.h"
8#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/loader/nro.h"
9 13
10namespace Service::LDR { 14namespace Service::LDR {
11 15
@@ -59,16 +63,58 @@ public:
59 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { 63 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} {
60 // clang-format off 64 // clang-format off
61 static const FunctionInfo functions[] = { 65 static const FunctionInfo functions[] = {
62 {0, nullptr, "LoadNro"}, 66 {0, &RelocatableObject::LoadNro, "LoadNro"},
63 {1, nullptr, "UnloadNro"}, 67 {1, nullptr, "UnloadNro"},
64 {2, nullptr, "LoadNrr"}, 68 {2, &RelocatableObject::LoadNrr, "LoadNrr"},
65 {3, nullptr, "UnloadNrr"}, 69 {3, nullptr, "UnloadNrr"},
66 {4, nullptr, "Initialize"}, 70 {4, &RelocatableObject::Initialize, "Initialize"},
67 }; 71 };
68 // clang-format on 72 // clang-format on
69 73
70 RegisterHandlers(functions); 74 RegisterHandlers(functions);
71 } 75 }
76
77 void LoadNrr(Kernel::HLERequestContext& ctx) {
78 IPC::ResponseBuilder rb{ctx, 2};
79 rb.Push(RESULT_SUCCESS);
80 LOG_WARNING(Service_LDR, "(STUBBED) called");
81 }
82
83 void LoadNro(Kernel::HLERequestContext& ctx) {
84 IPC::RequestParser rp{ctx};
85 rp.Skip(2, false);
86 const VAddr nro_addr{rp.Pop<VAddr>()};
87 const u64 nro_size{rp.Pop<u64>()};
88 const VAddr bss_addr{rp.Pop<VAddr>()};
89 const u64 bss_size{rp.Pop<u64>()};
90
91 // Read NRO data from memory
92 std::vector<u8> nro_data(nro_size);
93 Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
94
95 // Load NRO as new executable module
96 const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
97 Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
98
99 // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
100 // It is currently missing:
101 // - Signature checks with LoadNRR
102 // - Checking if a module has already been loaded
103 // - Using/validating BSS, etc. params (these are used from NRO header instead)
104 // - Error checking
105 // - ...Probably other things
106
107 IPC::ResponseBuilder rb{ctx, 4};
108 rb.Push(RESULT_SUCCESS);
109 rb.Push(addr);
110 LOG_WARNING(Service_LDR, "(STUBBED) called");
111 }
112
113 void Initialize(Kernel::HLERequestContext& ctx) {
114 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(RESULT_SUCCESS);
116 LOG_WARNING(Service_LDR, "(STUBBED) called");
117 }
72}; 118};
73 119
74void InstallInterfaces(SM::ServiceManager& sm) { 120void InstallInterfaces(SM::ServiceManager& sm) {
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index 7b91bb258..e1f17a926 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -14,14 +14,14 @@ public:
14 explicit MM_U() : ServiceFramework{"mm:u"} { 14 explicit MM_U() : ServiceFramework{"mm:u"} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {0, &MM_U::Initialize, "InitializeOld"}, 17 {0, &MM_U::Initialize, "Initialize"},
18 {1, &MM_U::Finalize, "FinalizeOld"}, 18 {1, &MM_U::Finalize, "Finalize"},
19 {2, &MM_U::SetAndWait, "SetAndWaitOld"}, 19 {2, &MM_U::SetAndWait, "SetAndWait"},
20 {3, &MM_U::Get, "GetOld"}, 20 {3, &MM_U::Get, "Get"},
21 {4, &MM_U::Initialize, "Initialize"}, 21 {4, &MM_U::InitializeWithId, "InitializeWithId"},
22 {5, &MM_U::Finalize, "Finalize"}, 22 {5, &MM_U::FinalizeWithId, "FinalizeWithId"},
23 {6, &MM_U::SetAndWait, "SetAndWait"}, 23 {6, &MM_U::SetAndWaitWithId, "SetAndWaitWithId"},
24 {7, &MM_U::Get, "Get"}, 24 {7, &MM_U::GetWithId, "GetWithId"},
25 }; 25 };
26 // clang-format on 26 // clang-format on
27 27
@@ -59,9 +59,43 @@ private:
59 rb.Push(current); 59 rb.Push(current);
60 } 60 }
61 61
62 void InitializeWithId(Kernel::HLERequestContext& ctx) {
63 LOG_WARNING(Service_MM, "(STUBBED) called");
64 IPC::ResponseBuilder rb{ctx, 3};
65 rb.Push(RESULT_SUCCESS);
66 rb.Push<u32>(id); // Any non zero value
67 }
68
69 void FinalizeWithId(Kernel::HLERequestContext& ctx) {
70 LOG_WARNING(Service_MM, "(STUBBED) called");
71 IPC::ResponseBuilder rb{ctx, 2};
72 rb.Push(RESULT_SUCCESS);
73 }
74
75 void SetAndWaitWithId(Kernel::HLERequestContext& ctx) {
76 IPC::RequestParser rp{ctx};
77 u32 input_id = rp.Pop<u32>();
78 min = rp.Pop<u32>();
79 max = rp.Pop<u32>();
80 current = min;
81
82 LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
83 input_id, min, max);
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(RESULT_SUCCESS);
86 }
87
88 void GetWithId(Kernel::HLERequestContext& ctx) {
89 LOG_WARNING(Service_MM, "(STUBBED) called");
90 IPC::ResponseBuilder rb{ctx, 3};
91 rb.Push(RESULT_SUCCESS);
92 rb.Push(current);
93 }
94
62 u32 min{0}; 95 u32 min{0};
63 u32 max{0}; 96 u32 max{0};
64 u32 current{0}; 97 u32 current{0};
98 u32 id{1};
65}; 99};
66 100
67void InstallInterfaces(SM::ServiceManager& service_manager) { 101void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 8fec97db8..30e542542 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -10,12 +10,13 @@
10#include "core/hle/service/nfc/nfc.h" 10#include "core/hle/service/nfc/nfc.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h" 12#include "core/hle/service/sm/sm.h"
13#include "core/settings.h"
13 14
14namespace Service::NFC { 15namespace Service::NFC {
15 16
16class IAm final : public ServiceFramework<IAm> { 17class IAm final : public ServiceFramework<IAm> {
17public: 18public:
18 explicit IAm() : ServiceFramework{"IAm"} { 19 explicit IAm() : ServiceFramework{"NFC::IAm"} {
19 // clang-format off 20 // clang-format off
20 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"}, 22 {0, nullptr, "Initialize"},
@@ -52,7 +53,7 @@ private:
52 53
53class MFIUser final : public ServiceFramework<MFIUser> { 54class MFIUser final : public ServiceFramework<MFIUser> {
54public: 55public:
55 explicit MFIUser() : ServiceFramework{"IUser"} { 56 explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} {
56 // clang-format off 57 // clang-format off
57 static const FunctionInfo functions[] = { 58 static const FunctionInfo functions[] = {
58 {0, nullptr, "Initialize"}, 59 {0, nullptr, "Initialize"},
@@ -100,13 +101,13 @@ private:
100 101
101class IUser final : public ServiceFramework<IUser> { 102class IUser final : public ServiceFramework<IUser> {
102public: 103public:
103 explicit IUser() : ServiceFramework{"IUser"} { 104 explicit IUser() : ServiceFramework{"NFC::IUser"} {
104 // clang-format off 105 // clang-format off
105 static const FunctionInfo functions[] = { 106 static const FunctionInfo functions[] = {
106 {0, nullptr, "Initialize"}, 107 {0, &IUser::InitializeOld, "InitializeOld"},
107 {1, nullptr, "Finalize"}, 108 {1, &IUser::FinalizeOld, "FinalizeOld"},
108 {2, nullptr, "GetState"}, 109 {2, &IUser::GetStateOld, "GetStateOld"},
109 {3, nullptr, "IsNfcEnabled"}, 110 {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
110 {400, nullptr, "Initialize"}, 111 {400, nullptr, "Initialize"},
111 {401, nullptr, "Finalize"}, 112 {401, nullptr, "Finalize"},
112 {402, nullptr, "GetState"}, 113 {402, nullptr, "GetState"},
@@ -130,11 +131,47 @@ public:
130 131
131 RegisterHandlers(functions); 132 RegisterHandlers(functions);
132 } 133 }
134
135private:
136 enum class NfcStates : u32 {
137 Finalized = 6,
138 };
139
140 void InitializeOld(Kernel::HLERequestContext& ctx) {
141 IPC::ResponseBuilder rb{ctx, 2, 0};
142 rb.Push(RESULT_SUCCESS);
143
144 // We don't deal with hardware initialization so we can just stub this.
145 LOG_DEBUG(Service_NFC, "called");
146 }
147
148 void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
149 IPC::ResponseBuilder rb{ctx, 3};
150 rb.Push(RESULT_SUCCESS);
151 rb.PushRaw<u8>(Settings::values.enable_nfc);
152
153 LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
154 }
155
156 void GetStateOld(Kernel::HLERequestContext& ctx) {
157 LOG_WARNING(Service_NFC, "(STUBBED) called");
158
159 IPC::ResponseBuilder rb{ctx, 3};
160 rb.Push(RESULT_SUCCESS);
161 rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
162 }
163
164 void FinalizeOld(Kernel::HLERequestContext& ctx) {
165 LOG_WARNING(Service_NFC, "(STUBBED) called");
166
167 IPC::ResponseBuilder rb{ctx, 2};
168 rb.Push(RESULT_SUCCESS);
169 }
133}; 170};
134 171
135class NFC_U final : public ServiceFramework<NFC_U> { 172class NFC_U final : public ServiceFramework<NFC_U> {
136public: 173public:
137 explicit NFC_U() : ServiceFramework{"nfc:u"} { 174 explicit NFC_U() : ServiceFramework{"nfc:user"} {
138 // clang-format off 175 // clang-format off
139 static const FunctionInfo functions[] = { 176 static const FunctionInfo functions[] = {
140 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, 177 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 8c07a05c2..c1af878fe 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,56 +2,67 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <atomic>
6
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/lock.h"
9#include "core/hle/service/hid/hid.h" 12#include "core/hle/service/hid/hid.h"
10#include "core/hle/service/nfp/nfp.h" 13#include "core/hle/service/nfp/nfp.h"
11#include "core/hle/service/nfp/nfp_user.h" 14#include "core/hle/service/nfp/nfp_user.h"
12 15
13namespace Service::NFP { 16namespace Service::NFP {
14 17
18namespace ErrCodes {
19constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
20 -1); // TODO(ogniK): Find the actual error code
21}
22
15Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 23Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
16 : ServiceFramework(name), module(std::move(module)) {} 24 : ServiceFramework(name), module(std::move(module)) {
25 auto& kernel = Core::System::GetInstance().Kernel();
26 nfc_tag_load =
27 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected");
28}
17 29
18Module::Interface::~Interface() = default; 30Module::Interface::~Interface() = default;
19 31
20class IUser final : public ServiceFramework<IUser> { 32class IUser final : public ServiceFramework<IUser> {
21public: 33public:
22 IUser() : ServiceFramework("IUser") { 34 IUser(Module::Interface& nfp_interface)
35 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
23 static const FunctionInfo functions[] = { 36 static const FunctionInfo functions[] = {
24 {0, &IUser::Initialize, "Initialize"}, 37 {0, &IUser::Initialize, "Initialize"},
25 {1, nullptr, "Finalize"}, 38 {1, &IUser::Finalize, "Finalize"},
26 {2, &IUser::ListDevices, "ListDevices"}, 39 {2, &IUser::ListDevices, "ListDevices"},
27 {3, nullptr, "StartDetection"}, 40 {3, &IUser::StartDetection, "StartDetection"},
28 {4, nullptr, "StopDetection"}, 41 {4, &IUser::StopDetection, "StopDetection"},
29 {5, nullptr, "Mount"}, 42 {5, &IUser::Mount, "Mount"},
30 {6, nullptr, "Unmount"}, 43 {6, &IUser::Unmount, "Unmount"},
31 {7, nullptr, "OpenApplicationArea"}, 44 {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
32 {8, nullptr, "GetApplicationArea"}, 45 {8, &IUser::GetApplicationArea, "GetApplicationArea"},
33 {9, nullptr, "SetApplicationArea"}, 46 {9, nullptr, "SetApplicationArea"},
34 {10, nullptr, "Flush"}, 47 {10, nullptr, "Flush"},
35 {11, nullptr, "Restore"}, 48 {11, nullptr, "Restore"},
36 {12, nullptr, "CreateApplicationArea"}, 49 {12, nullptr, "CreateApplicationArea"},
37 {13, nullptr, "GetTagInfo"}, 50 {13, &IUser::GetTagInfo, "GetTagInfo"},
38 {14, nullptr, "GetRegisterInfo"}, 51 {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
39 {15, nullptr, "GetCommonInfo"}, 52 {15, &IUser::GetCommonInfo, "GetCommonInfo"},
40 {16, nullptr, "GetModelInfo"}, 53 {16, &IUser::GetModelInfo, "GetModelInfo"},
41 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, 54 {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
42 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, 55 {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
43 {19, &IUser::GetState, "GetState"}, 56 {19, &IUser::GetState, "GetState"},
44 {20, &IUser::GetDeviceState, "GetDeviceState"}, 57 {20, &IUser::GetDeviceState, "GetDeviceState"},
45 {21, &IUser::GetNpadId, "GetNpadId"}, 58 {21, &IUser::GetNpadId, "GetNpadId"},
46 {22, nullptr, "GetApplicationArea2"}, 59 {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
47 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, 60 {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
48 {24, nullptr, "RecreateApplicationArea"}, 61 {24, nullptr, "RecreateApplicationArea"},
49 }; 62 };
50 RegisterHandlers(functions); 63 RegisterHandlers(functions);
51 64
52 auto& kernel = Core::System::GetInstance().Kernel(); 65 auto& kernel = Core::System::GetInstance().Kernel();
53 activate_event =
54 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");
55 deactivate_event = 66 deactivate_event =
56 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); 67 Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
57 availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, 68 availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
@@ -59,6 +70,17 @@ public:
59 } 70 }
60 71
61private: 72private:
73 struct TagInfo {
74 std::array<u8, 10> uuid;
75 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
76 // mean something else
77 INSERT_PADDING_BYTES(0x15);
78 u32_le protocol;
79 u32_le tag_type;
80 INSERT_PADDING_BYTES(0x2c);
81 };
82 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
83
62 enum class State : u32 { 84 enum class State : u32 {
63 NonInitialized = 0, 85 NonInitialized = 0,
64 Initialized = 1, 86 Initialized = 1,
@@ -66,15 +88,40 @@ private:
66 88
67 enum class DeviceState : u32 { 89 enum class DeviceState : u32 {
68 Initialized = 0, 90 Initialized = 0,
91 SearchingForTag = 1,
92 TagFound = 2,
93 TagRemoved = 3,
94 TagNearby = 4,
95 Unknown5 = 5,
96 Finalized = 6
97 };
98
99 struct CommonInfo {
100 u16_be last_write_year;
101 u8 last_write_month;
102 u8 last_write_day;
103 u16_be write_counter;
104 u16_be version;
105 u32_be application_area_size;
106 INSERT_PADDING_BYTES(0x34);
69 }; 107 };
108 static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
70 109
71 void Initialize(Kernel::HLERequestContext& ctx) { 110 void Initialize(Kernel::HLERequestContext& ctx) {
72 LOG_WARNING(Service_NFP, "(STUBBED) called"); 111 IPC::ResponseBuilder rb{ctx, 2, 0};
112 rb.Push(RESULT_SUCCESS);
73 113
74 state = State::Initialized; 114 state = State::Initialized;
75 115
76 IPC::ResponseBuilder rb{ctx, 2}; 116 LOG_DEBUG(Service_NFC, "called");
117 }
118
119 void GetState(Kernel::HLERequestContext& ctx) {
120 IPC::ResponseBuilder rb{ctx, 3, 0};
77 rb.Push(RESULT_SUCCESS); 121 rb.Push(RESULT_SUCCESS);
122 rb.PushRaw<u32>(static_cast<u32>(state));
123
124 LOG_DEBUG(Service_NFC, "called");
78 } 125 }
79 126
80 void ListDevices(Kernel::HLERequestContext& ctx) { 127 void ListDevices(Kernel::HLERequestContext& ctx) {
@@ -83,80 +130,219 @@ private:
83 130
84 ctx.WriteBuffer(&device_handle, sizeof(device_handle)); 131 ctx.WriteBuffer(&device_handle, sizeof(device_handle));
85 132
86 LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); 133 LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
87 134
88 IPC::ResponseBuilder rb{ctx, 3}; 135 IPC::ResponseBuilder rb{ctx, 3};
89 rb.Push(RESULT_SUCCESS); 136 rb.Push(RESULT_SUCCESS);
90 rb.Push<u32>(0); 137 rb.Push<u32>(1);
91 } 138 }
92 139
93 void AttachActivateEvent(Kernel::HLERequestContext& ctx) { 140 void GetNpadId(Kernel::HLERequestContext& ctx) {
94 IPC::RequestParser rp{ctx}; 141 IPC::RequestParser rp{ctx};
95 const u64 dev_handle = rp.Pop<u64>(); 142 const u64 dev_handle = rp.Pop<u64>();
96 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 143 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
144 IPC::ResponseBuilder rb{ctx, 3};
145 rb.Push(RESULT_SUCCESS);
146 rb.Push<u32>(npad_id);
147 }
97 148
149 void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
150 IPC::RequestParser rp{ctx};
151 const u64 dev_handle = rp.Pop<u64>();
152 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
98 IPC::ResponseBuilder rb{ctx, 2, 1}; 153 IPC::ResponseBuilder rb{ctx, 2, 1};
99 rb.Push(RESULT_SUCCESS); 154 rb.Push(RESULT_SUCCESS);
100 rb.PushCopyObjects(activate_event); 155 rb.PushCopyObjects(nfp_interface.GetNFCEvent());
156 has_attached_handle = true;
101 } 157 }
102 158
103 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { 159 void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
104 IPC::RequestParser rp{ctx}; 160 IPC::RequestParser rp{ctx};
105 const u64 dev_handle = rp.Pop<u64>(); 161 const u64 dev_handle = rp.Pop<u64>();
106 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 162 LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
107 163
108 IPC::ResponseBuilder rb{ctx, 2, 1}; 164 IPC::ResponseBuilder rb{ctx, 2, 1};
109 rb.Push(RESULT_SUCCESS); 165 rb.Push(RESULT_SUCCESS);
110 rb.PushCopyObjects(deactivate_event); 166 rb.PushCopyObjects(deactivate_event);
111 } 167 }
112 168
113 void GetState(Kernel::HLERequestContext& ctx) { 169 void StopDetection(Kernel::HLERequestContext& ctx) {
114 LOG_WARNING(Service_NFP, "(STUBBED) called"); 170 LOG_DEBUG(Service_NFP, "called");
115 IPC::ResponseBuilder rb{ctx, 3}; 171 switch (device_state) {
172 case DeviceState::TagFound:
173 case DeviceState::TagNearby:
174 deactivate_event->Signal();
175 device_state = DeviceState::Initialized;
176 break;
177 case DeviceState::SearchingForTag:
178 case DeviceState::TagRemoved:
179 device_state = DeviceState::Initialized;
180 break;
181 }
182 IPC::ResponseBuilder rb{ctx, 2};
116 rb.Push(RESULT_SUCCESS); 183 rb.Push(RESULT_SUCCESS);
117 rb.Push<u32>(static_cast<u32>(state));
118 } 184 }
119 185
120 void GetDeviceState(Kernel::HLERequestContext& ctx) { 186 void GetDeviceState(Kernel::HLERequestContext& ctx) {
121 LOG_WARNING(Service_NFP, "(STUBBED) called"); 187 LOG_DEBUG(Service_NFP, "called");
188 auto nfc_event = nfp_interface.GetNFCEvent();
189 if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) {
190 device_state = DeviceState::TagFound;
191 nfc_event->Clear();
192 }
193
122 IPC::ResponseBuilder rb{ctx, 3}; 194 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(RESULT_SUCCESS); 195 rb.Push(RESULT_SUCCESS);
124 rb.Push<u32>(static_cast<u32>(device_state)); 196 rb.Push<u32>(static_cast<u32>(device_state));
125 } 197 }
126 198
127 void GetNpadId(Kernel::HLERequestContext& ctx) { 199 void StartDetection(Kernel::HLERequestContext& ctx) {
128 IPC::RequestParser rp{ctx}; 200 LOG_DEBUG(Service_NFP, "called");
129 const u64 dev_handle = rp.Pop<u64>(); 201
130 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); 202 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
131 IPC::ResponseBuilder rb{ctx, 3}; 203 device_state = DeviceState::SearchingForTag;
204 }
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(RESULT_SUCCESS);
207 }
208
209 void GetTagInfo(Kernel::HLERequestContext& ctx) {
210 LOG_DEBUG(Service_NFP, "called");
211
212 IPC::ResponseBuilder rb{ctx, 2};
213 auto amiibo = nfp_interface.GetAmiiboBuffer();
214 TagInfo tag_info{};
215 std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size()));
216 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
217
218 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
219 tag_info.tag_type = 2;
220 ctx.WriteBuffer(&tag_info, sizeof(TagInfo));
221 rb.Push(RESULT_SUCCESS);
222 }
223
224 void Mount(Kernel::HLERequestContext& ctx) {
225 LOG_DEBUG(Service_NFP, "called");
226
227 device_state = DeviceState::TagNearby;
228 IPC::ResponseBuilder rb{ctx, 2};
229 rb.Push(RESULT_SUCCESS);
230 }
231
232 void GetModelInfo(Kernel::HLERequestContext& ctx) {
233 LOG_DEBUG(Service_NFP, "called");
234
235 IPC::ResponseBuilder rb{ctx, 2};
236 auto amiibo = nfp_interface.GetAmiiboBuffer();
237 ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
238 rb.Push(RESULT_SUCCESS);
239 }
240
241 void Unmount(Kernel::HLERequestContext& ctx) {
242 LOG_DEBUG(Service_NFP, "called");
243
244 device_state = DeviceState::TagFound;
245
246 IPC::ResponseBuilder rb{ctx, 2};
247 rb.Push(RESULT_SUCCESS);
248 }
249
250 void Finalize(Kernel::HLERequestContext& ctx) {
251 LOG_DEBUG(Service_NFP, "called");
252
253 device_state = DeviceState::Finalized;
254
255 IPC::ResponseBuilder rb{ctx, 2};
132 rb.Push(RESULT_SUCCESS); 256 rb.Push(RESULT_SUCCESS);
133 rb.Push<u32>(npad_id);
134 } 257 }
135 258
136 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { 259 void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
137 IPC::RequestParser rp{ctx}; 260 LOG_WARNING(Service_NFP, "(STUBBED) called");
138 const u64 dev_handle = rp.Pop<u64>();
139 LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
140 261
141 IPC::ResponseBuilder rb{ctx, 2, 1}; 262 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS); 263 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(availability_change_event); 264 rb.PushCopyObjects(availability_change_event);
144 } 265 }
145 266
146 const u64 device_handle{0xDEAD}; 267 void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
147 const HID::ControllerID npad_id{HID::Controller_Player1}; 268 LOG_WARNING(Service_NFP, "(STUBBED) called");
269
270 // TODO(ogniK): Pull Mii and owner data from amiibo
271
272 IPC::ResponseBuilder rb{ctx, 2};
273 rb.Push(RESULT_SUCCESS);
274 }
275
276 void GetCommonInfo(Kernel::HLERequestContext& ctx) {
277 LOG_WARNING(Service_NFP, "(STUBBED) called");
278
279 // TODO(ogniK): Pull common information from amiibo
280
281 CommonInfo common_info{};
282 common_info.application_area_size = 0;
283 ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
284
285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(RESULT_SUCCESS);
287 }
288
289 void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
290 LOG_DEBUG(Service_NFP, "called");
291 // We don't need to worry about this since we can just open the file
292 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(RESULT_SUCCESS);
294 }
295
296 void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
297 LOG_WARNING(Service_NFP, "(STUBBED) called");
298 // We don't need to worry about this since we can just open the file
299 IPC::ResponseBuilder rb{ctx, 3};
300 rb.Push(RESULT_SUCCESS);
301 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
302 }
303
304 void GetApplicationArea(Kernel::HLERequestContext& ctx) {
305 LOG_WARNING(Service_NFP, "(STUBBED) called");
306
307 // TODO(ogniK): Pull application area from amiibo
308
309 IPC::ResponseBuilder rb{ctx, 3};
310 rb.Push(RESULT_SUCCESS);
311 rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
312 }
313
314 bool has_attached_handle{};
315 const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')};
316 const u32 npad_id{0}; // Player 1 controller
148 State state{State::NonInitialized}; 317 State state{State::NonInitialized};
149 DeviceState device_state{DeviceState::Initialized}; 318 DeviceState device_state{DeviceState::Initialized};
150 Kernel::SharedPtr<Kernel::Event> activate_event;
151 Kernel::SharedPtr<Kernel::Event> deactivate_event; 319 Kernel::SharedPtr<Kernel::Event> deactivate_event;
152 Kernel::SharedPtr<Kernel::Event> availability_change_event; 320 Kernel::SharedPtr<Kernel::Event> availability_change_event;
321 const Module::Interface& nfp_interface;
153}; 322};
154 323
155void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 324void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
156 LOG_DEBUG(Service_NFP, "called"); 325 LOG_DEBUG(Service_NFP, "called");
157 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 326 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
158 rb.Push(RESULT_SUCCESS); 327 rb.Push(RESULT_SUCCESS);
159 rb.PushIpcInterface<IUser>(); 328 rb.PushIpcInterface<IUser>(*this);
329}
330
331bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
332 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
333 if (buffer.size() < sizeof(AmiiboFile)) {
334 return false;
335 }
336
337 std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
338 nfc_tag_load->Signal();
339 return true;
340}
341const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
342 return nfc_tag_load;
343}
344const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
345 return amiibo;
160} 346}
161 347
162void InstallInterfaces(SM::ServiceManager& service_manager) { 348void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 77df343c4..5c0ae8a54 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -4,6 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector>
9#include "core/hle/kernel/event.h"
7#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
8 11
9namespace Service::NFP { 12namespace Service::NFP {
@@ -15,7 +18,27 @@ public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 18 explicit Interface(std::shared_ptr<Module> module, const char* name);
16 ~Interface() override; 19 ~Interface() override;
17 20
21 struct ModelInfo {
22 std::array<u8, 0x8> amiibo_identification_block;
23 INSERT_PADDING_BYTES(0x38);
24 };
25 static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
26
27 struct AmiiboFile {
28 std::array<u8, 10> uuid;
29 INSERT_PADDING_BYTES(0x4a);
30 ModelInfo model_info;
31 };
32 static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
33
18 void CreateUserInterface(Kernel::HLERequestContext& ctx); 34 void CreateUserInterface(Kernel::HLERequestContext& ctx);
35 bool LoadAmiibo(const std::vector<u8>& buffer);
36 const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
37 const AmiiboFile& GetAmiiboBuffer() const;
38
39 private:
40 Kernel::SharedPtr<Kernel::Event> nfc_tag_load{};
41 AmiiboFile amiibo{};
19 42
20 protected: 43 protected:
21 std::shared_ptr<Module> module; 44 std::shared_ptr<Module> module;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 10611ed6a..75dcd94a3 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -219,6 +219,7 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
219 {35, nullptr, "GetScanData"}, 219 {35, nullptr, "GetScanData"},
220 {36, nullptr, "GetCurrentAccessPoint"}, 220 {36, nullptr, "GetCurrentAccessPoint"},
221 {37, nullptr, "Shutdown"}, 221 {37, nullptr, "Shutdown"},
222 {38, nullptr, "GetAllowedChannels"},
222 }; 223 };
223 RegisterHandlers(functions); 224 RegisterHandlers(functions);
224} 225}
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 261ad539c..18091c9bb 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -71,6 +71,22 @@ public:
71 } 71 }
72}; 72};
73 73
74class NIM_ECA final : public ServiceFramework<NIM_ECA> {
75public:
76 explicit NIM_ECA() : ServiceFramework{"nim:eca"} {
77 // clang-format off
78 static const FunctionInfo functions[] = {
79 {0, nullptr, "CreateServerInterface"},
80 {1, nullptr, "RefreshDebugAvailability"},
81 {2, nullptr, "ClearDebugResponse"},
82 {3, nullptr, "RegisterDebugResponse"},
83 };
84 // clang-format on
85
86 RegisterHandlers(functions);
87 }
88};
89
74class NIM_SHP final : public ServiceFramework<NIM_SHP> { 90class NIM_SHP final : public ServiceFramework<NIM_SHP> {
75public: 91public:
76 explicit NIM_SHP() : ServiceFramework{"nim:shp"} { 92 explicit NIM_SHP() : ServiceFramework{"nim:shp"} {
@@ -214,6 +230,7 @@ private:
214 230
215void InstallInterfaces(SM::ServiceManager& sm) { 231void InstallInterfaces(SM::ServiceManager& sm) {
216 std::make_shared<NIM>()->InstallAsService(sm); 232 std::make_shared<NIM>()->InstallAsService(sm);
233 std::make_shared<NIM_ECA>()->InstallAsService(sm);
217 std::make_shared<NIM_SHP>()->InstallAsService(sm); 234 std::make_shared<NIM_SHP>()->InstallAsService(sm);
218 std::make_shared<NTC>()->InstallAsService(sm); 235 std::make_shared<NTC>()->InstallAsService(sm);
219} 236}
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
new file mode 100644
index 000000000..ccb6f9da9
--- /dev/null
+++ b/src/core/hle/service/npns/npns.cpp
@@ -0,0 +1,88 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "core/hle/service/npns/npns.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::NPNS {
12
13class NPNS_S final : public ServiceFramework<NPNS_S> {
14public:
15 explicit NPNS_S() : ServiceFramework{"npns:s"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {1, nullptr, "ListenAll"},
19 {2, nullptr, "ListenTo"},
20 {3, nullptr, "Receive"},
21 {4, nullptr, "ReceiveRaw"},
22 {5, nullptr, "GetReceiveEvent"},
23 {6, nullptr, "ListenUndelivered"},
24 {7, nullptr, "GetStateChangeEVent"},
25 {11, nullptr, "SubscribeTopic"},
26 {12, nullptr, "UnsubscribeTopic"},
27 {13, nullptr, "QueryIsTopicExist"},
28 {21, nullptr, "CreateToken"},
29 {22, nullptr, "CreateTokenWithApplicationId"},
30 {23, nullptr, "DestroyToken"},
31 {24, nullptr, "DestroyTokenWithApplicationId"},
32 {25, nullptr, "QueryIsTokenValid"},
33 {31, nullptr, "UploadTokenToBaaS"},
34 {32, nullptr, "DestroyTokenForBaaS"},
35 {33, nullptr, "CreateTokenForBaaS"},
36 {34, nullptr, "SetBaaSDeviceAccountIdList"},
37 {101, nullptr, "Suspend"},
38 {102, nullptr, "Resume"},
39 {103, nullptr, "GetState"},
40 {104, nullptr, "GetStatistics"},
41 {105, nullptr, "GetPlayReportRequestEvent"},
42 {111, nullptr, "GetJid"},
43 {112, nullptr, "CreateJid"},
44 {113, nullptr, "DestroyJid"},
45 {114, nullptr, "AttachJid"},
46 {115, nullptr, "DetachJid"},
47 {201, nullptr, "RequestChangeStateForceTimed"},
48 {102, nullptr, "RequestChangeStateForceAsync"},
49 };
50 // clang-format on
51
52 RegisterHandlers(functions);
53 }
54};
55
56class NPNS_U final : public ServiceFramework<NPNS_U> {
57public:
58 explicit NPNS_U() : ServiceFramework{"npns:u"} {
59 // clang-format off
60 static const FunctionInfo functions[] = {
61 {1, nullptr, "ListenAll"},
62 {2, nullptr, "ListenTo"},
63 {3, nullptr, "Receive"},
64 {4, nullptr, "ReceiveRaw"},
65 {5, nullptr, "GetReceiveEvent"},
66 {7, nullptr, "GetStateChangeEVent"},
67 {21, nullptr, "CreateToken"},
68 {23, nullptr, "DestroyToken"},
69 {25, nullptr, "QueryIsTokenValid"},
70 {26, nullptr, "ListenToMyApplicationId"},
71 {101, nullptr, "Suspend"},
72 {102, nullptr, "Resume"},
73 {103, nullptr, "GetState"},
74 {104, nullptr, "GetStatistics"},
75 {111, nullptr, "GetJid"},
76 };
77 // clang-format on
78
79 RegisterHandlers(functions);
80 }
81};
82
83void InstallInterfaces(SM::ServiceManager& sm) {
84 std::make_shared<NPNS_S>()->InstallAsService(sm);
85 std::make_shared<NPNS_U>()->InstallAsService(sm);
86}
87
88} // namespace Service::NPNS
diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h
new file mode 100644
index 000000000..861cd3e48
--- /dev/null
+++ b/src/core/hle/service/npns/npns.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::NPNS {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::NPNS
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 98017267c..07c1381fe 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -93,13 +93,23 @@ public:
93 {86, nullptr, "EnableApplicationCrashReport"}, 93 {86, nullptr, "EnableApplicationCrashReport"},
94 {87, nullptr, "IsApplicationCrashReportEnabled"}, 94 {87, nullptr, "IsApplicationCrashReportEnabled"},
95 {90, nullptr, "BoostSystemMemoryResourceLimit"}, 95 {90, nullptr, "BoostSystemMemoryResourceLimit"},
96 {91, nullptr, "Unknown1"},
97 {92, nullptr, "Unknown2"},
98 {93, nullptr, "GetMainApplicationProgramIndex"},
99 {94, nullptr, "LaunchApplication2"},
100 {95, nullptr, "GetApplicationLaunchInfo"},
101 {96, nullptr, "AcquireApplicationLaunchInfo"},
102 {97, nullptr, "GetMainApplicationProgramIndex2"},
103 {98, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
96 {100, nullptr, "ResetToFactorySettings"}, 104 {100, nullptr, "ResetToFactorySettings"},
97 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, 105 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
98 {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, 106 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
99 {200, nullptr, "CalculateUserSaveDataStatistics"}, 107 {200, nullptr, "CalculateUserSaveDataStatistics"},
100 {201, nullptr, "DeleteUserSaveDataAll"}, 108 {201, nullptr, "DeleteUserSaveDataAll"},
101 {210, nullptr, "DeleteUserSystemSaveData"}, 109 {210, nullptr, "DeleteUserSystemSaveData"},
110 {211, nullptr, "DeleteSaveData"},
102 {220, nullptr, "UnregisterNetworkServiceAccount"}, 111 {220, nullptr, "UnregisterNetworkServiceAccount"},
112 {221, nullptr, "UnregisterNetworkServiceAccountWithUserSaveDataDeletion"},
103 {300, nullptr, "GetApplicationShellEvent"}, 113 {300, nullptr, "GetApplicationShellEvent"},
104 {301, nullptr, "PopApplicationShellEventInfo"}, 114 {301, nullptr, "PopApplicationShellEventInfo"},
105 {302, nullptr, "LaunchLibraryApplet"}, 115 {302, nullptr, "LaunchLibraryApplet"},
@@ -114,6 +124,7 @@ public:
114 {403, nullptr, "GetMaxApplicationControlCacheCount"}, 124 {403, nullptr, "GetMaxApplicationControlCacheCount"},
115 {404, nullptr, "InvalidateApplicationControlCache"}, 125 {404, nullptr, "InvalidateApplicationControlCache"},
116 {405, nullptr, "ListApplicationControlCacheEntryInfo"}, 126 {405, nullptr, "ListApplicationControlCacheEntryInfo"},
127 {406, nullptr, "GetApplicationControlProperty"},
117 {502, nullptr, "RequestCheckGameCardRegistration"}, 128 {502, nullptr, "RequestCheckGameCardRegistration"},
118 {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, 129 {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
119 {504, nullptr, "RequestRegisterGameCard"}, 130 {504, nullptr, "RequestRegisterGameCard"},
@@ -129,6 +140,7 @@ public:
129 {604, nullptr, "RegisterContentsExternalKey"}, 140 {604, nullptr, "RegisterContentsExternalKey"},
130 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, 141 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
131 {606, nullptr, "GetContentMetaStorage"}, 142 {606, nullptr, "GetContentMetaStorage"},
143 {607, nullptr, "ListAvailableAddOnContent"},
132 {700, nullptr, "PushDownloadTaskList"}, 144 {700, nullptr, "PushDownloadTaskList"},
133 {701, nullptr, "ClearTaskStatusList"}, 145 {701, nullptr, "ClearTaskStatusList"},
134 {702, nullptr, "RequestDownloadTaskList"}, 146 {702, nullptr, "RequestDownloadTaskList"},
@@ -148,6 +160,9 @@ public:
148 {907, nullptr, "WithdrawApplicationUpdateRequest"}, 160 {907, nullptr, "WithdrawApplicationUpdateRequest"},
149 {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, 161 {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
150 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, 162 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
163 {910, nullptr, "Unknown3"},
164 {911, nullptr, "SetPreInstalledApplication"},
165 {912, nullptr, "ClearPreInstalledApplicationFlag"},
151 {1000, nullptr, "RequestVerifyApplicationDeprecated"}, 166 {1000, nullptr, "RequestVerifyApplicationDeprecated"},
152 {1001, nullptr, "CorruptApplicationForDebug"}, 167 {1001, nullptr, "CorruptApplicationForDebug"},
153 {1002, nullptr, "RequestVerifyAddOnContentsRights"}, 168 {1002, nullptr, "RequestVerifyAddOnContentsRights"},
@@ -162,6 +177,8 @@ public:
162 {1305, nullptr, "TryDeleteRunningApplicationEntity"}, 177 {1305, nullptr, "TryDeleteRunningApplicationEntity"},
163 {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, 178 {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
164 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, 179 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
180 {1308, nullptr, "DeleteApplicationCompletelyForDebug"},
181 {1309, nullptr, "CleanupUnavailableAddOnContents"},
165 {1400, nullptr, "PrepareShutdown"}, 182 {1400, nullptr, "PrepareShutdown"},
166 {1500, nullptr, "FormatSdCard"}, 183 {1500, nullptr, "FormatSdCard"},
167 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, 184 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
@@ -199,6 +216,28 @@ public:
199 {2015, nullptr, "CompareSystemDeliveryInfo"}, 216 {2015, nullptr, "CompareSystemDeliveryInfo"},
200 {2016, nullptr, "ListNotCommittedContentMeta"}, 217 {2016, nullptr, "ListNotCommittedContentMeta"},
201 {2017, nullptr, "CreateDownloadTask"}, 218 {2017, nullptr, "CreateDownloadTask"},
219 {2018, nullptr, "Unknown4"},
220 {2050, nullptr, "Unknown5"},
221 {2100, nullptr, "Unknown6"},
222 {2101, nullptr, "Unknown7"},
223 {2150, nullptr, "CreateRightsEnvironment"},
224 {2151, nullptr, "DestroyRightsEnvironment"},
225 {2152, nullptr, "ActivateRightsEnvironment"},
226 {2153, nullptr, "DeactivateRightsEnvironment"},
227 {2154, nullptr, "ForceActivateRightsContextForExit"},
228 {2160, nullptr, "AddTargetApplicationToRightsEnvironment"},
229 {2161, nullptr, "SetUsersToRightsEnvironment"},
230 {2170, nullptr, "GetRightsEnvironmentStatus"},
231 {2171, nullptr, "GetRightsEnvironmentStatusChangedEvent"},
232 {2180, nullptr, "RequestExtendRightsInRightsEnvironment"},
233 {2181, nullptr, "GetLastResultOfExtendRightsInRightsEnvironment"},
234 {2182, nullptr, "SetActiveRightsContextUsingStateToRightsEnvironment"},
235 {2190, nullptr, "GetRightsEnvironmentHandleForApplication"},
236 {2199, nullptr, "GetRightsEnvironmentCountForDebug"},
237 {2200, nullptr, "Unknown8"},
238 {2201, nullptr, "Unknown9"},
239 {2250, nullptr, "Unknown10"},
240 {2300, nullptr, "Unknown11"},
202 }; 241 };
203 // clang-format on 242 // clang-format on
204 243
@@ -348,12 +387,15 @@ public:
348 {0, nullptr, "LaunchProgram"}, 387 {0, nullptr, "LaunchProgram"},
349 {1, nullptr, "TerminateProcess"}, 388 {1, nullptr, "TerminateProcess"},
350 {2, nullptr, "TerminateProgram"}, 389 {2, nullptr, "TerminateProgram"},
351 {3, nullptr, "GetShellEventHandle"}, 390 {4, nullptr, "GetShellEventHandle"},
352 {4, nullptr, "GetShellEventInfo"}, 391 {5, nullptr, "GetShellEventInfo"},
353 {5, nullptr, "TerminateApplication"}, 392 {6, nullptr, "TerminateApplication"},
354 {6, nullptr, "PrepareLaunchProgramFromHost"}, 393 {7, nullptr, "PrepareLaunchProgramFromHost"},
355 {7, nullptr, "LaunchApplication"}, 394 {8, nullptr, "LaunchApplication"},
356 {8, nullptr, "LaunchApplicationWithStorageId"}, 395 {9, nullptr, "LaunchApplicationWithStorageId"},
396 {10, nullptr, "TerminateApplication2"},
397 {11, nullptr, "GetRunningApplicationProcessId"},
398 {12, nullptr, "SetCurrentApplicationRightsEnvironmentCanBeActive"},
357 }; 399 };
358 // clang-format on 400 // clang-format on
359 401
@@ -388,6 +430,7 @@ public:
388 {19, nullptr, "GetReceivedEulaDataSize"}, 430 {19, nullptr, "GetReceivedEulaDataSize"},
389 {20, nullptr, "GetReceivedEulaData"}, 431 {20, nullptr, "GetReceivedEulaData"},
390 {21, nullptr, "SetupToReceiveSystemUpdate"}, 432 {21, nullptr, "SetupToReceiveSystemUpdate"},
433 {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
391 }; 434 };
392 // clang-format on 435 // clang-format on
393 436
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 4b2f758a8..44accecb7 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -161,7 +161,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
161 }; 161 };
162 RegisterHandlers(functions); 162 RegisterHandlers(functions);
163 // Attempt to load shared font data from disk 163 // Attempt to load shared font data from disk
164 const auto nand = FileSystem::GetSystemNANDContents(); 164 const auto* nand = FileSystem::GetSystemNANDContents();
165 std::size_t offset = 0; 165 std::size_t offset = 0;
166 // Rebuild shared fonts from data ncas 166 // Rebuild shared fonts from data ncas
167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index d8b8037a8..c41ef7058 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -15,6 +15,11 @@
15#include "video_core/renderer_base.h" 15#include "video_core/renderer_base.h"
16 16
17namespace Service::Nvidia::Devices { 17namespace Service::Nvidia::Devices {
18namespace NvErrCodes {
19enum {
20 InvalidNmapHandle = -22,
21};
22}
18 23
19nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} 24nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {}
20nvhost_as_gpu::~nvhost_as_gpu() = default; 25nvhost_as_gpu::~nvhost_as_gpu() = default;
@@ -79,14 +84,16 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
79 std::memcpy(entries.data(), input.data(), input.size()); 84 std::memcpy(entries.data(), input.data(), input.size());
80 85
81 auto& gpu = Core::System::GetInstance().GPU(); 86 auto& gpu = Core::System::GetInstance().GPU();
82
83 for (const auto& entry : entries) { 87 for (const auto& entry : entries) {
84 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", 88 LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
85 entry.offset, entry.nvmap_handle, entry.pages); 89 entry.offset, entry.nvmap_handle, entry.pages);
86 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; 90 Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10;
87
88 auto object = nvmap_dev->GetObject(entry.nvmap_handle); 91 auto object = nvmap_dev->GetObject(entry.nvmap_handle);
89 ASSERT(object); 92 if (!object) {
93 LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle);
94 std::memcpy(output.data(), entries.data(), output.size());
95 return static_cast<u32>(NvErrCodes::InvalidNmapHandle);
96 }
90 97
91 ASSERT(object->status == nvmap::Object::Status::Allocated); 98 ASSERT(object->status == nvmap::Object::Status::Allocated);
92 99
@@ -157,15 +164,21 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
157 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 164 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
158 165
159 const auto itr = buffer_mappings.find(params.offset); 166 const auto itr = buffer_mappings.find(params.offset);
160 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); 167 if (itr == buffer_mappings.end()) {
168 LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset);
169 // Hardware tests shows that unmapping an already unmapped buffer always returns successful
170 // and doesn't fail.
171 return 0;
172 }
161 173
162 auto& system_instance = Core::System::GetInstance(); 174 auto& system_instance = Core::System::GetInstance();
163 175
164 // Remove this memory region from the rasterizer cache. 176 // Remove this memory region from the rasterizer cache.
165 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
166 itr->second.size);
167
168 auto& gpu = system_instance.GPU(); 177 auto& gpu = system_instance.GPU();
178 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
179 ASSERT(cpu_addr);
180 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size);
181
169 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 182 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
170 183
171 buffer_mappings.erase(itr->second.offset); 184 buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index a2287cc1b..43651d8a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -11,6 +11,13 @@
11 11
12namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
13 13
14namespace NvErrCodes {
15enum {
16 OperationNotPermitted = -1,
17 InvalidValue = -22,
18};
19}
20
14nvmap::nvmap() = default; 21nvmap::nvmap() = default;
15nvmap::~nvmap() = default; 22nvmap::~nvmap() = default;
16 23
@@ -44,7 +51,11 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o
44u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { 51u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
45 IocCreateParams params; 52 IocCreateParams params;
46 std::memcpy(&params, input.data(), sizeof(params)); 53 std::memcpy(&params, input.data(), sizeof(params));
54 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
47 55
56 if (!params.size) {
57 return static_cast<u32>(NvErrCodes::InvalidValue);
58 }
48 // Create a new nvmap object and obtain a handle to it. 59 // Create a new nvmap object and obtain a handle to it.
49 auto object = std::make_shared<Object>(); 60 auto object = std::make_shared<Object>();
50 object->id = next_id++; 61 object->id = next_id++;
@@ -55,8 +66,6 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
55 u32 handle = next_handle++; 66 u32 handle = next_handle++;
56 handles[handle] = std::move(object); 67 handles[handle] = std::move(object);
57 68
58 LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
59
60 params.handle = handle; 69 params.handle = handle;
61 70
62 std::memcpy(output.data(), &params, sizeof(params)); 71 std::memcpy(output.data(), &params, sizeof(params));
@@ -66,9 +75,29 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
66u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { 75u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
67 IocAllocParams params; 76 IocAllocParams params;
68 std::memcpy(&params, input.data(), sizeof(params)); 77 std::memcpy(&params, input.data(), sizeof(params));
78 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
79
80 if (!params.handle) {
81 return static_cast<u32>(NvErrCodes::InvalidValue);
82 }
83
84 if ((params.align - 1) & params.align) {
85 return static_cast<u32>(NvErrCodes::InvalidValue);
86 }
87
88 const u32 min_alignment = 0x1000;
89 if (params.align < min_alignment) {
90 params.align = min_alignment;
91 }
69 92
70 auto object = GetObject(params.handle); 93 auto object = GetObject(params.handle);
71 ASSERT(object); 94 if (!object) {
95 return static_cast<u32>(NvErrCodes::InvalidValue);
96 }
97
98 if (object->status == Object::Status::Allocated) {
99 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
100 }
72 101
73 object->flags = params.flags; 102 object->flags = params.flags;
74 object->align = params.align; 103 object->align = params.align;
@@ -76,8 +105,6 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
76 object->addr = params.addr; 105 object->addr = params.addr;
77 object->status = Object::Status::Allocated; 106 object->status = Object::Status::Allocated;
78 107
79 LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
80
81 std::memcpy(output.data(), &params, sizeof(params)); 108 std::memcpy(output.data(), &params, sizeof(params));
82 return 0; 109 return 0;
83} 110}
@@ -88,8 +115,14 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
88 115
89 LOG_WARNING(Service_NVDRV, "called"); 116 LOG_WARNING(Service_NVDRV, "called");
90 117
118 if (!params.handle) {
119 return static_cast<u32>(NvErrCodes::InvalidValue);
120 }
121
91 auto object = GetObject(params.handle); 122 auto object = GetObject(params.handle);
92 ASSERT(object); 123 if (!object) {
124 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
125 }
93 126
94 params.id = object->id; 127 params.id = object->id;
95 128
@@ -105,7 +138,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
105 138
106 auto itr = std::find_if(handles.begin(), handles.end(), 139 auto itr = std::find_if(handles.begin(), handles.end(),
107 [&](const auto& entry) { return entry.second->id == params.id; }); 140 [&](const auto& entry) { return entry.second->id == params.id; });
108 ASSERT(itr != handles.end()); 141 if (itr == handles.end()) {
142 return static_cast<u32>(NvErrCodes::InvalidValue);
143 }
144
145 auto& object = itr->second;
146 if (object->status != Object::Status::Allocated) {
147 return static_cast<u32>(NvErrCodes::InvalidValue);
148 }
109 149
110 itr->second->refcount++; 150 itr->second->refcount++;
111 151
@@ -125,8 +165,13 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
125 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param); 165 LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
126 166
127 auto object = GetObject(params.handle); 167 auto object = GetObject(params.handle);
128 ASSERT(object); 168 if (!object) {
129 ASSERT(object->status == Object::Status::Allocated); 169 return static_cast<u32>(NvErrCodes::InvalidValue);
170 }
171
172 if (object->status != Object::Status::Allocated) {
173 return static_cast<u32>(NvErrCodes::OperationNotPermitted);
174 }
130 175
131 switch (static_cast<ParamTypes>(params.param)) { 176 switch (static_cast<ParamTypes>(params.param)) {
132 case ParamTypes::Size: 177 case ParamTypes::Size:
@@ -163,9 +208,12 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
163 LOG_WARNING(Service_NVDRV, "(STUBBED) called"); 208 LOG_WARNING(Service_NVDRV, "(STUBBED) called");
164 209
165 auto itr = handles.find(params.handle); 210 auto itr = handles.find(params.handle);
166 ASSERT(itr != handles.end()); 211 if (itr == handles.end()) {
167 212 return static_cast<u32>(NvErrCodes::InvalidValue);
168 ASSERT(itr->second->refcount > 0); 213 }
214 if (!itr->second->refcount) {
215 return static_cast<u32>(NvErrCodes::InvalidValue);
216 }
169 217
170 itr->second->refcount--; 218 itr->second->refcount--;
171 219
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index fd98d541d..630ebbfc7 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -31,7 +31,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
31 buffer_wait_event->Signal(); 31 buffer_wait_event->Signal();
32} 32}
33 33
34boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { 34std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
35 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { 35 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
36 // Only consider free buffers. Buffers become free once again after they've been Acquired 36 // Only consider free buffers. Buffers become free once again after they've been Acquired
37 // and Released by the compositor, see the NVFlinger::Compose method. 37 // and Released by the compositor, see the NVFlinger::Compose method.
@@ -44,7 +44,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
44 }); 44 });
45 45
46 if (itr == queue.end()) { 46 if (itr == queue.end()) {
47 return boost::none; 47 return {};
48 } 48 }
49 49
50 itr->status = Buffer::Status::Dequeued; 50 itr->status = Buffer::Status::Dequeued;
@@ -70,12 +70,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
70 itr->crop_rect = crop_rect; 70 itr->crop_rect = crop_rect;
71} 71}
72 72
73boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { 73std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
74 auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { 74 auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
75 return buffer.status == Buffer::Status::Queued; 75 return buffer.status == Buffer::Status::Queued;
76 }); 76 });
77 if (itr == queue.end()) 77 if (itr == queue.end())
78 return boost::none; 78 return {};
79 itr->status = Buffer::Status::Acquired; 79 itr->status = Buffer::Status::Acquired;
80 return *itr; 80 return *itr;
81} 81}
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 50b767732..8cff5eb71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,8 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include <vector> 8#include <vector>
8#include <boost/optional.hpp> 9
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/math_util.h" 11#include "common/math_util.h"
11#include "common/swap.h" 12#include "common/swap.h"
@@ -57,9 +58,9 @@ public:
57 /// Rotate source image 90 degrees clockwise 58 /// Rotate source image 90 degrees clockwise
58 Rotate90 = 0x04, 59 Rotate90 = 0x04,
59 /// Rotate source image 180 degrees 60 /// Rotate source image 180 degrees
60 Roate180 = 0x03, 61 Rotate180 = 0x03,
61 /// Rotate source image 270 degrees clockwise 62 /// Rotate source image 270 degrees clockwise
62 Roate270 = 0x07, 63 Rotate270 = 0x07,
63 }; 64 };
64 65
65 struct Buffer { 66 struct Buffer {
@@ -73,11 +74,11 @@ public:
73 }; 74 };
74 75
75 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); 76 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
76 boost::optional<u32> DequeueBuffer(u32 width, u32 height); 77 std::optional<u32> DequeueBuffer(u32 width, u32 height);
77 const IGBPBuffer& RequestBuffer(u32 slot) const; 78 const IGBPBuffer& RequestBuffer(u32 slot) const;
78 void QueueBuffer(u32 slot, BufferTransformFlags transform, 79 void QueueBuffer(u32 slot, BufferTransformFlags transform,
79 const MathUtil::Rectangle<int>& crop_rect); 80 const MathUtil::Rectangle<int>& crop_rect);
80 boost::optional<const Buffer&> AcquireBuffer(); 81 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
81 void ReleaseBuffer(u32 slot); 82 void ReleaseBuffer(u32 slot);
82 u32 Query(QueryType type); 83 u32 Query(QueryType type);
83 84
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index d47b6f659..214e6d1b3 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -3,7 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <boost/optional.hpp> 6#include <optional>
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/assert.h" 9#include "common/assert.h"
@@ -134,7 +134,7 @@ void NVFlinger::Compose() {
134 134
135 MicroProfileFlip(); 135 MicroProfileFlip();
136 136
137 if (buffer == boost::none) { 137 if (!buffer) {
138 auto& system_instance = Core::System::GetInstance(); 138 auto& system_instance = Core::System::GetInstance();
139 139
140 // There was no queued buffer to draw, render previous frame 140 // There was no queued buffer to draw, render previous frame
@@ -143,7 +143,7 @@ void NVFlinger::Compose() {
143 continue; 143 continue;
144 } 144 }
145 145
146 auto& igbp_buffer = buffer->igbp_buffer; 146 auto& igbp_buffer = buffer->get().igbp_buffer;
147 147
148 // Now send the buffer to the GPU for drawing. 148 // Now send the buffer to the GPU for drawing.
149 // TODO(Subv): Support more than just disp0. The display device selection is probably based 149 // TODO(Subv): Support more than just disp0. The display device selection is probably based
@@ -152,10 +152,10 @@ void NVFlinger::Compose() {
152 ASSERT(nvdisp); 152 ASSERT(nvdisp);
153 153
154 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, 154 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
155 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform, 155 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
156 buffer->crop_rect); 156 buffer->get().transform, buffer->get().crop_rect);
157 157
158 buffer_queue->ReleaseBuffer(buffer->slot); 158 buffer_queue->ReleaseBuffer(buffer->get().slot);
159 } 159 }
160} 160}
161 161
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 6a9eccfb5..e4fcee9f8 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -14,20 +14,24 @@ public:
14 explicit PlayReport(const char* name) : ServiceFramework{name} { 14 explicit PlayReport(const char* name) : ServiceFramework{name} {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {10100, nullptr, "SaveReport"}, 17 {10100, nullptr, "SaveReportOld"},
18 {10101, &PlayReport::SaveReportWithUser, "SaveReportWithUser"}, 18 {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"},
19 {10102, nullptr, "SaveReport"},
20 {10103, nullptr, "SaveReportWithUser"},
19 {10200, nullptr, "RequestImmediateTransmission"}, 21 {10200, nullptr, "RequestImmediateTransmission"},
20 {10300, nullptr, "GetTransmissionStatus"}, 22 {10300, nullptr, "GetTransmissionStatus"},
21 {20100, nullptr, "SaveSystemReport"}, 23 {20100, nullptr, "SaveSystemReport"},
22 {20200, nullptr, "SetOperationMode"},
23 {20101, nullptr, "SaveSystemReportWithUser"}, 24 {20101, nullptr, "SaveSystemReportWithUser"},
25 {20200, nullptr, "SetOperationMode"},
24 {30100, nullptr, "ClearStorage"}, 26 {30100, nullptr, "ClearStorage"},
27 {30200, nullptr, "ClearStatistics"},
28 {30300, nullptr, "GetStorageUsage"},
29 {30400, nullptr, "GetStatistics"},
30 {30401, nullptr, "GetThroughputHistory"},
31 {30500, nullptr, "GetLastUploadError"},
25 {40100, nullptr, "IsUserAgreementCheckEnabled"}, 32 {40100, nullptr, "IsUserAgreementCheckEnabled"},
26 {40101, nullptr, "SetUserAgreementCheckEnabled"}, 33 {40101, nullptr, "SetUserAgreementCheckEnabled"},
27 {90100, nullptr, "GetStorageUsage"}, 34 {90100, nullptr, "ReadAllReportFiles"},
28 {90200, nullptr, "GetStatistics"},
29 {90201, nullptr, "GetThroughputHistory"},
30 {90300, nullptr, "GetLastUploadError"},
31 }; 35 };
32 // clang-format on 36 // clang-format on
33 37
@@ -35,7 +39,7 @@ public:
35 } 39 }
36 40
37private: 41private:
38 void SaveReportWithUser(Kernel::HLERequestContext& ctx) { 42 void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
39 // TODO(ogniK): Do we want to add play report? 43 // TODO(ogniK): Do we want to add play report?
40 LOG_WARNING(Service_PREPO, "(STUBBED) called"); 44 LOG_WARNING(Service_PREPO, "(STUBBED) called");
41 45
@@ -46,6 +50,7 @@ private:
46 50
47void InstallInterfaces(SM::ServiceManager& service_manager) { 51void InstallInterfaces(SM::ServiceManager& service_manager) {
48 std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); 52 std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager);
53 std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager);
49 std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); 54 std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager);
50 std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); 55 std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager);
51 std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); 56 std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager);
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
new file mode 100644
index 000000000..c2d5fda94
--- /dev/null
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -0,0 +1,71 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/service/ptm/psm.h"
10#include "core/hle/service/service.h"
11#include "core/hle/service/sm/sm.h"
12
13namespace Service::PSM {
14
15constexpr u32 BATTERY_FULLY_CHARGED = 100; // 100% Full
16constexpr u32 BATTERY_CURRENTLY_CHARGING = 1; // Plugged into an official dock
17
18class PSM final : public ServiceFramework<PSM> {
19public:
20 explicit PSM() : ServiceFramework{"psm"} {
21 // clang-format off
22 static const FunctionInfo functions[] = {
23 {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
24 {1, &PSM::GetChargerType, "GetChargerType"},
25 {2, nullptr, "EnableBatteryCharging"},
26 {3, nullptr, "DisableBatteryCharging"},
27 {4, nullptr, "IsBatteryChargingEnabled"},
28 {5, nullptr, "AcquireControllerPowerSupply"},
29 {6, nullptr, "ReleaseControllerPowerSupply"},
30 {7, nullptr, "OpenSession"},
31 {8, nullptr, "EnableEnoughPowerChargeEmulation"},
32 {9, nullptr, "DisableEnoughPowerChargeEmulation"},
33 {10, nullptr, "EnableFastBatteryCharging"},
34 {11, nullptr, "DisableFastBatteryCharging"},
35 {12, nullptr, "GetBatteryVoltageState"},
36 {13, nullptr, "GetRawBatteryChargePercentage"},
37 {14, nullptr, "IsEnoughPowerSupplied"},
38 {15, nullptr, "GetBatteryAgePercentage"},
39 {16, nullptr, "GetBatteryChargeInfoEvent"},
40 {17, nullptr, "GetBatteryChargeInfoFields"},
41 };
42 // clang-format on
43
44 RegisterHandlers(functions);
45 }
46
47 ~PSM() override = default;
48
49private:
50 void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
51 LOG_WARNING(Service_PSM, "(STUBBED) called");
52
53 IPC::ResponseBuilder rb{ctx, 3};
54 rb.Push(RESULT_SUCCESS);
55 rb.Push<u32>(BATTERY_FULLY_CHARGED);
56 }
57
58 void GetChargerType(Kernel::HLERequestContext& ctx) {
59 LOG_WARNING(Service_PSM, "(STUBBED) called");
60
61 IPC::ResponseBuilder rb{ctx, 3};
62 rb.Push(RESULT_SUCCESS);
63 rb.Push<u32>(BATTERY_CURRENTLY_CHARGING);
64 }
65};
66
67void InstallInterfaces(SM::ServiceManager& sm) {
68 std::make_shared<PSM>()->InstallAsService(sm);
69}
70
71} // namespace Service::PSM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
new file mode 100644
index 000000000..a286793ae
--- /dev/null
+++ b/src/core/hle/service/ptm/psm.h
@@ -0,0 +1,15 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::PSM {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::PSM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 62f049660..a4cf45267 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -22,7 +22,7 @@
22#include "core/hle/service/apm/apm.h" 22#include "core/hle/service/apm/apm.h"
23#include "core/hle/service/arp/arp.h" 23#include "core/hle/service/arp/arp.h"
24#include "core/hle/service/audio/audio.h" 24#include "core/hle/service/audio/audio.h"
25#include "core/hle/service/bcat/bcat.h" 25#include "core/hle/service/bcat/module.h"
26#include "core/hle/service/bpc/bpc.h" 26#include "core/hle/service/bpc/bpc.h"
27#include "core/hle/service/btdrv/btdrv.h" 27#include "core/hle/service/btdrv/btdrv.h"
28#include "core/hle/service/btm/btm.h" 28#include "core/hle/service/btm/btm.h"
@@ -48,15 +48,17 @@
48#include "core/hle/service/nfp/nfp.h" 48#include "core/hle/service/nfp/nfp.h"
49#include "core/hle/service/nifm/nifm.h" 49#include "core/hle/service/nifm/nifm.h"
50#include "core/hle/service/nim/nim.h" 50#include "core/hle/service/nim/nim.h"
51#include "core/hle/service/npns/npns.h"
51#include "core/hle/service/ns/ns.h" 52#include "core/hle/service/ns/ns.h"
52#include "core/hle/service/nvdrv/nvdrv.h" 53#include "core/hle/service/nvdrv/nvdrv.h"
53#include "core/hle/service/nvflinger/nvflinger.h" 54#include "core/hle/service/nvflinger/nvflinger.h"
54#include "core/hle/service/pcie/pcie.h" 55#include "core/hle/service/pcie/pcie.h"
55#include "core/hle/service/pctl/pctl.h" 56#include "core/hle/service/pctl/module.h"
56#include "core/hle/service/pcv/pcv.h" 57#include "core/hle/service/pcv/pcv.h"
57#include "core/hle/service/pm/pm.h" 58#include "core/hle/service/pm/pm.h"
58#include "core/hle/service/prepo/prepo.h" 59#include "core/hle/service/prepo/prepo.h"
59#include "core/hle/service/psc/psc.h" 60#include "core/hle/service/psc/psc.h"
61#include "core/hle/service/ptm/psm.h"
60#include "core/hle/service/service.h" 62#include "core/hle/service/service.h"
61#include "core/hle/service/set/settings.h" 63#include "core/hle/service/set/settings.h"
62#include "core/hle/service/sm/sm.h" 64#include "core/hle/service/sm/sm.h"
@@ -197,7 +199,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
197// Module interface 199// Module interface
198 200
199/// Initialize ServiceManager 201/// Initialize ServiceManager
200void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { 202void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {
201 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 203 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
202 // here and pass it into the respective InstallInterfaces functions. 204 // here and pass it into the respective InstallInterfaces functions.
203 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); 205 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
@@ -220,7 +222,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
220 EUPLD::InstallInterfaces(*sm); 222 EUPLD::InstallInterfaces(*sm);
221 Fatal::InstallInterfaces(*sm); 223 Fatal::InstallInterfaces(*sm);
222 FGM::InstallInterfaces(*sm); 224 FGM::InstallInterfaces(*sm);
223 FileSystem::InstallInterfaces(*sm, rfs); 225 FileSystem::InstallInterfaces(*sm, vfs);
224 Friend::InstallInterfaces(*sm); 226 Friend::InstallInterfaces(*sm);
225 GRC::InstallInterfaces(*sm); 227 GRC::InstallInterfaces(*sm);
226 HID::InstallInterfaces(*sm); 228 HID::InstallInterfaces(*sm);
@@ -236,6 +238,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
236 NFP::InstallInterfaces(*sm); 238 NFP::InstallInterfaces(*sm);
237 NIFM::InstallInterfaces(*sm); 239 NIFM::InstallInterfaces(*sm);
238 NIM::InstallInterfaces(*sm); 240 NIM::InstallInterfaces(*sm);
241 NPNS::InstallInterfaces(*sm);
239 NS::InstallInterfaces(*sm); 242 NS::InstallInterfaces(*sm);
240 Nvidia::InstallInterfaces(*sm, *nv_flinger); 243 Nvidia::InstallInterfaces(*sm, *nv_flinger);
241 PCIe::InstallInterfaces(*sm); 244 PCIe::InstallInterfaces(*sm);
@@ -244,6 +247,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
244 PlayReport::InstallInterfaces(*sm); 247 PlayReport::InstallInterfaces(*sm);
245 PM::InstallInterfaces(*sm); 248 PM::InstallInterfaces(*sm);
246 PSC::InstallInterfaces(*sm); 249 PSC::InstallInterfaces(*sm);
250 PSM::InstallInterfaces(*sm);
247 Set::InstallInterfaces(*sm); 251 Set::InstallInterfaces(*sm);
248 Sockets::InstallInterfaces(*sm); 252 Sockets::InstallInterfaces(*sm);
249 SPL::InstallInterfaces(*sm); 253 SPL::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 2fc57a82e..98483ecf1 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -180,8 +180,7 @@ private:
180}; 180};
181 181
182/// Initialize ServiceManager 182/// Initialize ServiceManager
183void Init(std::shared_ptr<SM::ServiceManager>& sm, 183void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);
184 const std::shared_ptr<FileSys::VfsFilesystem>& vfs);
185 184
186/// Shutdown ServiceManager 185/// Shutdown ServiceManager
187void Shutdown(); 186void Shutdown();
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index 5af356d10..34654bb07 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -39,7 +39,8 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") {
39 {29, nullptr, "GetAmiiboEcqvBlsKey"}, 39 {29, nullptr, "GetAmiiboEcqvBlsKey"},
40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, 40 {30, nullptr, "GetAmiiboEcqvBlsCertificate"},
41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, 41 {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"},
42 {32, nullptr, "GetUnknownId"}, 42 {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"},
43 {33, nullptr, "GetBatteryVersion"},
43 }; 44 };
44 RegisterHandlers(functions); 45 RegisterHandlers(functions);
45} 46}
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index e7fb5a419..f0a831d45 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -67,15 +67,15 @@ public:
67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { 67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
68 // clang-format off 68 // clang-format off
69 static const FunctionInfo functions[] = { 69 static const FunctionInfo functions[] = {
70 {0, nullptr, "Unknown1"}, 70 {0, nullptr, "Open"},
71 {1, nullptr, "Unknown2"}, 71 {1, nullptr, "Close"},
72 {2, nullptr, "Unknown3"}, 72 {2, nullptr, "Unknown1"},
73 {3, nullptr, "Unknown4"}, 73 {3, nullptr, "Populate"},
74 {4, nullptr, "PostBufferAsync"}, 74 {4, nullptr, "PostBufferAsync"},
75 {5, nullptr, "Unknown5"}, 75 {5, nullptr, "GetXferReport"},
76 {6, nullptr, "Unknown6"}, 76 {6, nullptr, "Unknown2"},
77 {7, nullptr, "Unknown7"}, 77 {7, nullptr, "Unknown3"},
78 {8, nullptr, "Unknown8"}, 78 {8, nullptr, "Unknown4"},
79 }; 79 };
80 // clang-format on 80 // clang-format on
81 81
@@ -89,15 +89,15 @@ public:
89 // clang-format off 89 // clang-format off
90 static const FunctionInfo functions[] = { 90 static const FunctionInfo functions[] = {
91 {0, nullptr, "Unknown1"}, 91 {0, nullptr, "Unknown1"},
92 {1, nullptr, "Unknown2"}, 92 {1, nullptr, "SetInterface"},
93 {2, nullptr, "Unknown3"}, 93 {2, nullptr, "GetInterface"},
94 {3, nullptr, "Unknown4"}, 94 {3, nullptr, "GetAlternateInterface"},
95 {4, nullptr, "Unknown5"}, 95 {4, nullptr, "GetCurrentFrame"},
96 {5, nullptr, "CtrlXferAsync"}, 96 {5, nullptr, "CtrlXferAsync"},
97 {6, nullptr, "Unknown6"}, 97 {6, nullptr, "Unknown2"},
98 {7, nullptr, "GetCtrlXferReport"}, 98 {7, nullptr, "GetCtrlXferReport"},
99 {8, nullptr, "Unknown7"}, 99 {8, nullptr, "ResetDevice"},
100 {9, nullptr, "GetClientEpSession"}, 100 {9, nullptr, "OpenUsbEp"},
101 }; 101 };
102 // clang-format on 102 // clang-format on
103 103
@@ -111,13 +111,14 @@ public:
111 // clang-format off 111 // clang-format off
112 static const FunctionInfo functions[] = { 112 static const FunctionInfo functions[] = {
113 {0, nullptr, "BindClientProcess"}, 113 {0, nullptr, "BindClientProcess"},
114 {1, nullptr, "Unknown1"}, 114 {1, nullptr, "QueryAllInterfaces"},
115 {2, nullptr, "Unknown2"}, 115 {2, nullptr, "QueryAvailableInterfaces"},
116 {3, nullptr, "Unknown3"}, 116 {3, nullptr, "QueryAcquiredInterfaces"},
117 {4, nullptr, "Unknown4"}, 117 {4, nullptr, "CreateInterfaceAvailableEvent"},
118 {5, nullptr, "Unknown5"}, 118 {5, nullptr, "DestroyInterfaceAvailableEvent"},
119 {6, nullptr, "GetInterfaceStateChangeEvent"}, 119 {6, nullptr, "GetInterfaceStateChangeEvent"},
120 {7, nullptr, "GetClientIfSession"}, 120 {7, nullptr, "AcquireUsbIf"},
121 {8, nullptr, "Unknown1"},
121 }; 122 };
122 // clang-format on 123 // clang-format on
123 124
@@ -131,11 +132,11 @@ public:
131 // clang-format off 132 // clang-format off
132 static const FunctionInfo functions[] = { 133 static const FunctionInfo functions[] = {
133 {0, nullptr, "BindNoticeEvent"}, 134 {0, nullptr, "BindNoticeEvent"},
134 {1, nullptr, "Unknown1"}, 135 {1, nullptr, "UnbindNoticeEvent"},
135 {2, nullptr, "GetStatus"}, 136 {2, nullptr, "GetStatus"},
136 {3, nullptr, "GetNotice"}, 137 {3, nullptr, "GetNotice"},
137 {4, nullptr, "Unknown2"}, 138 {4, nullptr, "EnablePowerRequestNotice"},
138 {5, nullptr, "Unknown3"}, 139 {5, nullptr, "DisablePowerRequestNotice"},
139 {6, nullptr, "ReplyPowerRequest"}, 140 {6, nullptr, "ReplyPowerRequest"},
140 }; 141 };
141 // clang-format on 142 // clang-format on
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index bbc02abcc..d764b2406 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -6,9 +6,10 @@
6#include <array> 6#include <array>
7#include <cstring> 7#include <cstring>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <type_traits> 10#include <type_traits>
10#include <utility> 11#include <utility>
11#include <boost/optional.hpp> 12
12#include "common/alignment.h" 13#include "common/alignment.h"
13#include "common/assert.h" 14#include "common/assert.h"
14#include "common/common_funcs.h" 15#include "common/common_funcs.h"
@@ -506,9 +507,9 @@ private:
506 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; 507 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
507 const u32 width{request.data.width}; 508 const u32 width{request.data.width};
508 const u32 height{request.data.height}; 509 const u32 height{request.data.height};
509 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 510 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
510 511
511 if (slot != boost::none) { 512 if (slot) {
512 // Buffer is available 513 // Buffer is available
513 IGBPDequeueBufferResponseParcel response{*slot}; 514 IGBPDequeueBufferResponseParcel response{*slot};
514 ctx.WriteBuffer(response.Serialize()); 515 ctx.WriteBuffer(response.Serialize());
@@ -520,7 +521,7 @@ private:
520 Kernel::ThreadWakeupReason reason) { 521 Kernel::ThreadWakeupReason reason) {
521 // Repeat TransactParcel DequeueBuffer when a buffer is available 522 // Repeat TransactParcel DequeueBuffer when a buffer is available
522 auto buffer_queue = nv_flinger->GetBufferQueue(id); 523 auto buffer_queue = nv_flinger->GetBufferQueue(id);
523 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 524 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
524 IGBPDequeueBufferResponseParcel response{*slot}; 525 IGBPDequeueBufferResponseParcel response{*slot};
525 ctx.WriteBuffer(response.Serialize()); 526 ctx.WriteBuffer(response.Serialize());
526 IPC::ResponseBuilder rb{ctx, 2}; 527 IPC::ResponseBuilder rb{ctx, 2};
@@ -968,6 +969,54 @@ private:
968 rb.PushCopyObjects(vsync_event); 969 rb.PushCopyObjects(vsync_event);
969 } 970 }
970 971
972 enum class ConvertedScaleMode : u64 {
973 None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no
974 // scaling/default
975 Freeze = 1,
976 ScaleToWindow = 2,
977 Crop = 3,
978 NoCrop = 4,
979 };
980
981 // This struct is different, currently it's 1:1 but this might change in the future.
982 enum class NintendoScaleMode : u32 {
983 None = 0,
984 Freeze = 1,
985 ScaleToWindow = 2,
986 Crop = 3,
987 NoCrop = 4,
988 };
989
990 void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
991 IPC::RequestParser rp{ctx};
992 auto mode = rp.PopEnum<NintendoScaleMode>();
993 LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode));
994
995 IPC::ResponseBuilder rb{ctx, 4};
996 rb.Push(RESULT_SUCCESS);
997 switch (mode) {
998 case NintendoScaleMode::None:
999 rb.PushEnum(ConvertedScaleMode::None);
1000 break;
1001 case NintendoScaleMode::Freeze:
1002 rb.PushEnum(ConvertedScaleMode::Freeze);
1003 break;
1004 case NintendoScaleMode::ScaleToWindow:
1005 rb.PushEnum(ConvertedScaleMode::ScaleToWindow);
1006 break;
1007 case NintendoScaleMode::Crop:
1008 rb.PushEnum(ConvertedScaleMode::Crop);
1009 break;
1010 case NintendoScaleMode::NoCrop:
1011 rb.PushEnum(ConvertedScaleMode::NoCrop);
1012 break;
1013 default:
1014 UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode));
1015 rb.PushEnum(ConvertedScaleMode::None);
1016 break;
1017 }
1018 }
1019
971 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 1020 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
972}; 1021};
973 1022
@@ -991,7 +1040,7 @@ IApplicationDisplayService::IApplicationDisplayService(
991 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, 1040 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
992 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, 1041 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
993 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, 1042 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
994 {2102, nullptr, "ConvertScalingMode"}, 1043 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
995 {2450, nullptr, "GetIndirectLayerImageMap"}, 1044 {2450, nullptr, "GetIndirectLayerImageMap"},
996 {2451, nullptr, "GetIndirectLayerImageCropMap"}, 1045 {2451, nullptr, "GetIndirectLayerImageCropMap"},
997 {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, 1046 {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"},
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 9a86e5824..8518dddcb 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <cstring>
6#include "common/common_funcs.h" 7#include "common/common_funcs.h"
7#include "common/file_util.h" 8#include "common/file_util.h"
8#include "common/logging/log.h" 9#include "common/logging/log.h"
@@ -138,13 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
138 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
139 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
140 const FileSys::VirtualFile module_file = dir->GetFile(module); 141 const FileSys::VirtualFile module_file = dir->GetFile(module);
141 if (module_file != nullptr) { 142 if (module_file == nullptr) {
142 const VAddr load_addr = next_load_addr; 143 continue;
143 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, pm);
144 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
145 // Register module with GDBStub
146 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
147 } 144 }
145
146 const VAddr load_addr = next_load_addr;
147 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
148 const auto tentative_next_load_addr =
149 AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
150 if (!tentative_next_load_addr) {
151 return ResultStatus::ErrorLoadingNSO;
152 }
153
154 next_load_addr = *tentative_next_load_addr;
155 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
156 // Register module with GDBStub
157 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
148 } 158 }
149 159
150 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); 160 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index e67b49fc9..6057c7f26 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,16 +9,11 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/hle/kernel/kernel.h"
14#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/vm_manager.h" 13#include "core/hle/kernel/vm_manager.h"
16#include "core/loader/elf.h" 14#include "core/loader/elf.h"
17#include "core/memory.h" 15#include "core/memory.h"
18 16
19using Kernel::CodeSet;
20using Kernel::SharedPtr;
21
22//////////////////////////////////////////////////////////////////////////////////////////////////// 17////////////////////////////////////////////////////////////////////////////////////////////////////
23// ELF Header Constants 18// ELF Header Constants
24 19
@@ -211,7 +206,7 @@ public:
211 u32 GetFlags() const { 206 u32 GetFlags() const {
212 return (u32)(header->e_flags); 207 return (u32)(header->e_flags);
213 } 208 }
214 SharedPtr<CodeSet> LoadInto(VAddr vaddr); 209 Kernel::CodeSet LoadInto(VAddr vaddr);
215 210
216 int GetNumSegments() const { 211 int GetNumSegments() const {
217 return (int)(header->e_phnum); 212 return (int)(header->e_phnum);
@@ -274,7 +269,7 @@ const char* ElfReader::GetSectionName(int section) const {
274 return nullptr; 269 return nullptr;
275} 270}
276 271
277SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { 272Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
278 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 273 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
279 274
280 // Should we relocate? 275 // Should we relocate?
@@ -302,8 +297,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
302 std::vector<u8> program_image(total_image_size); 297 std::vector<u8> program_image(total_image_size);
303 std::size_t current_image_position = 0; 298 std::size_t current_image_position = 0;
304 299
305 auto& kernel = Core::System::GetInstance().Kernel(); 300 Kernel::CodeSet codeset;
306 SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
307 301
308 for (unsigned int i = 0; i < header->e_phnum; ++i) { 302 for (unsigned int i = 0; i < header->e_phnum; ++i) {
309 const Elf32_Phdr* p = &segments[i]; 303 const Elf32_Phdr* p = &segments[i];
@@ -311,14 +305,14 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
311 p->p_vaddr, p->p_filesz, p->p_memsz); 305 p->p_vaddr, p->p_filesz, p->p_memsz);
312 306
313 if (p->p_type == PT_LOAD) { 307 if (p->p_type == PT_LOAD) {
314 CodeSet::Segment* codeset_segment; 308 Kernel::CodeSet::Segment* codeset_segment;
315 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 309 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
316 if (permission_flags == (PF_R | PF_X)) { 310 if (permission_flags == (PF_R | PF_X)) {
317 codeset_segment = &codeset->CodeSegment(); 311 codeset_segment = &codeset.CodeSegment();
318 } else if (permission_flags == (PF_R)) { 312 } else if (permission_flags == (PF_R)) {
319 codeset_segment = &codeset->RODataSegment(); 313 codeset_segment = &codeset.RODataSegment();
320 } else if (permission_flags == (PF_R | PF_W)) { 314 } else if (permission_flags == (PF_R | PF_W)) {
321 codeset_segment = &codeset->DataSegment(); 315 codeset_segment = &codeset.DataSegment();
322 } else { 316 } else {
323 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 317 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
324 p->p_flags); 318 p->p_flags);
@@ -345,8 +339,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
345 } 339 }
346 } 340 }
347 341
348 codeset->entrypoint = base_addr + header->e_entry; 342 codeset.entrypoint = base_addr + header->e_entry;
349 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 343 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
350 344
351 LOG_DEBUG(Loader, "Done loading."); 345 LOG_DEBUG(Loader, "Done loading.");
352 346
@@ -397,11 +391,11 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
397 391
398 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 392 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
399 ElfReader elf_reader(&buffer[0]); 393 ElfReader elf_reader(&buffer[0]);
400 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address); 394 Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
401 codeset->name = file->GetName(); 395 const VAddr entry_point = codeset.entrypoint;
402 396
403 process.LoadModule(codeset, codeset->entrypoint); 397 process.LoadModule(std::move(codeset), entry_point);
404 process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); 398 process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
405 399
406 is_loaded = true; 400 is_loaded = true;
407 return ResultStatus::Success; 401 return ResultStatus::Success;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index f2a183ba1..9cd0b0ccd 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 93 return "unknown";
94} 94}
95 95
96constexpr std::array<const char*, 58> RESULT_MESSAGES{ 96constexpr std::array<const char*, 60> RESULT_MESSAGES{
97 "The operation completed successfully.", 97 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 98 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 99 "The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 58> RESULT_MESSAGES{
128 "The RomFS could not be found.", 128 "The RomFS could not be found.",
129 "The ELF file has incorrect size as determined by the header.", 129 "The ELF file has incorrect size as determined by the header.",
130 "There was a general error loading the NRO into emulated memory.", 130 "There was a general error loading the NRO into emulated memory.",
131 "There was a general error loading the NSO into emulated memory.",
131 "There is no icon available.", 132 "There is no icon available.",
132 "There is no control data available.", 133 "There is no control data available.",
133 "The NAX file has a bad header.", 134 "The NAX file has a bad header.",
@@ -152,6 +153,7 @@ constexpr std::array<const char*, 58> RESULT_MESSAGES{
152 "The BKTR-type NCA has a bad Relocation bucket.", 153 "The BKTR-type NCA has a bad Relocation bucket.",
153 "The BKTR-type NCA has a bad Subsection bucket.", 154 "The BKTR-type NCA has a bad Subsection bucket.",
154 "The BKTR-type NCA is missing the base RomFS.", 155 "The BKTR-type NCA is missing the base RomFS.",
156 "The NSP or XCI does not contain an update in addition to the base game.",
155}; 157};
156 158
157std::ostream& operator<<(std::ostream& os, ResultStatus status) { 159std::ostream& operator<<(std::ostream& os, ResultStatus status) {
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 20e66109b..7686634bf 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -6,10 +6,11 @@
6 6
7#include <iosfwd> 7#include <iosfwd>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10#include <utility> 11#include <utility>
11#include <vector> 12#include <vector>
12#include <boost/optional.hpp> 13
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "core/file_sys/vfs.h" 15#include "core/file_sys/vfs.h"
15 16
@@ -90,6 +91,7 @@ enum class ResultStatus : u16 {
90 ErrorNoRomFS, 91 ErrorNoRomFS,
91 ErrorIncorrectELFFileSize, 92 ErrorIncorrectELFFileSize,
92 ErrorLoadingNRO, 93 ErrorLoadingNRO,
94 ErrorLoadingNSO,
93 ErrorNoIcon, 95 ErrorNoIcon,
94 ErrorNoControl, 96 ErrorNoControl,
95 ErrorBadNAXHeader, 97 ErrorBadNAXHeader,
@@ -114,6 +116,7 @@ enum class ResultStatus : u16 {
114 ErrorBadRelocationBuckets, 116 ErrorBadRelocationBuckets,
115 ErrorBadSubsectionBuckets, 117 ErrorBadSubsectionBuckets,
116 ErrorMissingBKTRBaseRomFS, 118 ErrorMissingBKTRBaseRomFS,
119 ErrorNoPackedUpdate,
117}; 120};
118 121
119std::ostream& operator<<(std::ostream& os, ResultStatus status); 122std::ostream& operator<<(std::ostream& os, ResultStatus status);
@@ -143,7 +146,7 @@ public:
143 * information. 146 * information.
144 * @returns A pair with the optional system mode, and and the status. 147 * @returns A pair with the optional system mode, and and the status.
145 */ 148 */
146 virtual std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() { 149 virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
147 // 96MB allocated to the application. 150 // 96MB allocated to the application.
148 return std::make_pair(2, ResultStatus::Success); 151 return std::make_pair(2, ResultStatus::Success);
149 } 152 }
@@ -196,10 +199,19 @@ public:
196 /** 199 /**
197 * Get the RomFS of the application 200 * Get the RomFS of the application
198 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer 201 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
199 * @param dir The directory containing the RomFS 202 * @param file The directory containing the RomFS
203 * @return ResultStatus result of function
204 */
205 virtual ResultStatus ReadRomFS(FileSys::VirtualFile& file) {
206 return ResultStatus::ErrorNotImplemented;
207 }
208
209 /**
210 * Get the raw update of the application, should it come packed with one
211 * @param file The raw update NCA file (Program-type
200 * @return ResultStatus result of function 212 * @return ResultStatus result of function
201 */ 213 */
202 virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) { 214 virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) {
203 return ResultStatus::ErrorNotImplemented; 215 return ResultStatus::ErrorNotImplemented;
204 } 216 }
205 217
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 073fb9d2f..42f4a777b 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -72,6 +72,10 @@ ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
72 return nca_loader->ReadRomFS(dir); 72 return nca_loader->ReadRomFS(dir);
73} 73}
74 74
75u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
76 return nca_loader->ReadRomFSIVFCOffset();
77}
78
75ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) { 79ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
76 return nca_loader->ReadProgramId(out_program_id); 80 return nca_loader->ReadProgramId(out_program_id);
77} 81}
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index fc3c01876..b4d93bd01 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -36,6 +36,7 @@ public:
36 ResultStatus Load(Kernel::Process& process) override; 36 ResultStatus Load(Kernel::Process& process) override;
37 37
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override;
39 ResultStatus ReadProgramId(u64& out_program_id) override; 40 ResultStatus ReadProgramId(u64& out_program_id) override;
40 41
41private: 42private:
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index c10f826a4..bc8e402a8 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,11 +14,12 @@
14#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/vfs_offset.h" 15#include "core/file_sys/vfs_offset.h"
16#include "core/gdbstub/gdbstub.h" 16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
20#include "core/loader/nro.h" 19#include "core/loader/nro.h"
20#include "core/loader/nso.h"
21#include "core/memory.h" 21#include "core/memory.h"
22#include "core/settings.h"
22 23
23namespace Loader { 24namespace Loader {
24 25
@@ -126,28 +127,45 @@ static constexpr u32 PageAlignSize(u32 size) {
126 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
127} 128}
128 129
129bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { 130/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name,
130 // Read NSO header 131 VAddr load_base) {
131 NroHeader nro_header{}; 132
132 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { 133 if (data.size() < sizeof(NroHeader)) {
133 return {}; 134 return {};
134 } 135 }
136
137 // Read NSO header
138 NroHeader nro_header{};
139 std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
135 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 140 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
136 return {}; 141 return {};
137 } 142 }
138 143
139 // Build program image 144 // Build program image
140 auto& kernel = Core::System::GetInstance().Kernel(); 145 std::vector<u8> program_image(PageAlignSize(nro_header.file_size));
141 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, ""); 146 std::memcpy(program_image.data(), data.data(), program_image.size());
142 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
143 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 147 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
144 return {}; 148 return {};
145 } 149 }
146 150
151 Kernel::CodeSet codeset;
147 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { 152 for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
148 codeset->segments[i].addr = nro_header.segments[i].offset; 153 codeset.segments[i].addr = nro_header.segments[i].offset;
149 codeset->segments[i].offset = nro_header.segments[i].offset; 154 codeset.segments[i].offset = nro_header.segments[i].offset;
150 codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); 155 codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
156 }
157
158 if (!Settings::values.program_args.empty()) {
159 const auto arg_data = Settings::values.program_args;
160 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
161 NSOArgumentHeader args_header{
162 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
163 const auto end_offset = program_image.size();
164 program_image.resize(static_cast<u32>(program_image.size()) +
165 NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
166 std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
167 std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
168 arg_data.size());
151 } 169 }
152 170
153 // Read MOD header 171 // Read MOD header
@@ -161,20 +179,23 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
161 // Resize program image to include .bss section and page align each section 179 // Resize program image to include .bss section and page align each section
162 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 180 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
163 } 181 }
164 codeset->DataSegment().size += bss_size; 182 codeset.DataSegment().size += bss_size;
165 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 183 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
166 184
167 // Load codeset for current process 185 // Load codeset for current process
168 codeset->name = file->GetName(); 186 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
169 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 187 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
170 Core::CurrentProcess()->LoadModule(codeset, load_base);
171 188
172 // Register module with GDBStub 189 // Register module with GDBStub
173 GDBStub::RegisterModule(codeset->name, load_base, load_base); 190 GDBStub::RegisterModule(name, load_base, load_base);
174 191
175 return true; 192 return true;
176} 193}
177 194
195bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
196 return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base);
197}
198
178ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { 199ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
179 if (is_loaded) { 200 if (is_loaded) {
180 return ResultStatus::ErrorAlreadyLoaded; 201 return ResultStatus::ErrorAlreadyLoaded;
@@ -183,7 +204,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
183 // Load NRO 204 // Load NRO
184 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 205 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
185 206
186 if (!LoadNro(file, base_address)) { 207 if (!LoadNro(*file, base_address)) {
187 return ResultStatus::ErrorLoadingNRO; 208 return ResultStatus::ErrorLoadingNRO;
188 } 209 }
189 210
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04b46119a..3e6959302 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
10#include "core/loader/loader.h" 11#include "core/loader/loader.h"
@@ -40,8 +41,10 @@ public:
40 ResultStatus ReadTitle(std::string& title) override; 41 ResultStatus ReadTitle(std::string& title) override;
41 bool IsRomFSUpdatable() const override; 42 bool IsRomFSUpdatable() const override;
42 43
44 static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base);
45
43private: 46private:
44 bool LoadNro(FileSys::VirtualFile file, VAddr load_base); 47 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 48
46 std::vector<u8> icon_data; 49 std::vector<u8> icon_data;
47 std::unique_ptr<FileSys::NACP> nacp; 50 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 2186b02af..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -12,11 +12,11 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
14#include "core/gdbstub/gdbstub.h" 14#include "core/gdbstub/gdbstub.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/vm_manager.h" 16#include "core/hle/kernel/vm_manager.h"
18#include "core/loader/nso.h" 17#include "core/loader/nso.h"
19#include "core/memory.h" 18#include "core/memory.h"
19#include "core/settings.h"
20 20
21namespace Loader { 21namespace Loader {
22 22
@@ -93,36 +93,46 @@ static constexpr u32 PageAlignSize(u32 size) {
93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
94} 94}
95 95
96VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
97 boost::optional<FileSys::PatchManager> pm) { 97 bool should_pass_arguments,
98 if (file == nullptr) 98 std::optional<FileSys::PatchManager> pm) {
99 return {}; 99 if (file.GetSize() < sizeof(NsoHeader))
100
101 if (file->GetSize() < sizeof(NsoHeader))
102 return {}; 100 return {};
103 101
104 NsoHeader nso_header{}; 102 NsoHeader nso_header{};
105 if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) 103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
106 return {}; 104 return {};
107 105
108 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
109 return {}; 107 return {};
110 108
111 // Build program image 109 // Build program image
112 auto& kernel = Core::System::GetInstance().Kernel(); 110 Kernel::CodeSet codeset;
113 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, "");
114 std::vector<u8> program_image; 111 std::vector<u8> program_image;
115 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 112 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
116 std::vector<u8> data = 113 std::vector<u8> data =
117 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 114 file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
118 if (nso_header.IsSegmentCompressed(i)) { 115 if (nso_header.IsSegmentCompressed(i)) {
119 data = DecompressSegment(data, nso_header.segments[i]); 116 data = DecompressSegment(data, nso_header.segments[i]);
120 } 117 }
121 program_image.resize(nso_header.segments[i].location); 118 program_image.resize(nso_header.segments[i].location);
122 program_image.insert(program_image.end(), data.begin(), data.end()); 119 program_image.insert(program_image.end(), data.begin(), data.end());
123 codeset->segments[i].addr = nso_header.segments[i].location; 120 codeset.segments[i].addr = nso_header.segments[i].location;
124 codeset->segments[i].offset = nso_header.segments[i].location; 121 codeset.segments[i].offset = nso_header.segments[i].location;
125 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); 122 codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
123 }
124
125 if (should_pass_arguments && !Settings::values.program_args.empty()) {
126 const auto arg_data = Settings::values.program_args;
127 codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
128 NSOArgumentHeader args_header{
129 NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
130 const auto end_offset = program_image.size();
131 program_image.resize(static_cast<u32>(program_image.size()) +
132 NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
133 std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
134 std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
135 arg_data.size());
126 } 136 }
127 137
128 // MOD header pointer is at .text offset + 4 138 // MOD header pointer is at .text offset + 4
@@ -139,12 +149,12 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
139 // Resize program image to include .bss section and page align each section 149 // Resize program image to include .bss section and page align each section
140 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 150 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
141 } 151 }
142 codeset->DataSegment().size += bss_size; 152 codeset.DataSegment().size += bss_size;
143 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; 153 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
144 program_image.resize(image_size); 154 program_image.resize(image_size);
145 155
146 // Apply patches if necessary 156 // Apply patches if necessary
147 if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && pm->HasNSOPatch(nso_header.build_id)) {
148 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
149 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
150 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
@@ -155,12 +165,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
155 } 165 }
156 166
157 // Load codeset for current process 167 // Load codeset for current process
158 codeset->name = file->GetName(); 168 codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
159 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 169 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
160 Core::CurrentProcess()->LoadModule(codeset, load_base);
161 170
162 // Register module with GDBStub 171 // Register module with GDBStub
163 GDBStub::RegisterModule(codeset->name, load_base, load_base); 172 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
164 173
165 return load_base + image_size; 174 return load_base + image_size;
166} 175}
@@ -172,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
172 181
173 // Load module 182 // Load module
174 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 183 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
175 LoadModule(file, base_address); 184 if (!LoadModule(*file, base_address, true)) {
185 return ResultStatus::ErrorLoadingNSO;
186 }
176 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 187 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
177 188
178 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 189 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 05353d4d9..433306139 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/file_sys/patch_manager.h" 9#include "core/file_sys/patch_manager.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
@@ -11,6 +12,15 @@
11 12
12namespace Loader { 13namespace Loader {
13 14
15constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
16
17struct NSOArgumentHeader {
18 u32_le allocated_size;
19 u32_le actual_size;
20 INSERT_PADDING_BYTES(0x18);
21};
22static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
23
14/// Loads an NSO file 24/// Loads an NSO file
15class AppLoader_NSO final : public AppLoader, Linker { 25class AppLoader_NSO final : public AppLoader, Linker {
16public: 26public:
@@ -27,8 +37,9 @@ public:
27 return IdentifyType(file); 37 return IdentifyType(file);
28 } 38 }
29 39
30 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, 40 static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
31 boost::optional<FileSys::PatchManager> pm = boost::none); 41 bool should_pass_arguments,
42 std::optional<FileSys::PatchManager> pm = {});
32 43
33 ResultStatus Load(Kernel::Process& process) override; 44 ResultStatus Load(Kernel::Process& process) override;
34}; 45};
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index b7ba77ef4..080d89904 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -10,8 +10,10 @@
10#include "core/file_sys/control_metadata.h" 10#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
13#include "core/file_sys/registered_cache.h"
13#include "core/file_sys/submission_package.h" 14#include "core/file_sys/submission_package.h"
14#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
16#include "core/hle/service/filesystem/filesystem.h"
15#include "core/loader/deconstructed_rom_directory.h" 17#include "core/loader/deconstructed_rom_directory.h"
16#include "core/loader/nca.h" 18#include "core/loader/nca.h"
17#include "core/loader/nsp.h" 19#include "core/loader/nsp.h"
@@ -33,7 +35,17 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
33 return; 35 return;
34 36
35 std::tie(nacp_file, icon_file) = 37 std::tie(nacp_file, icon_file) =
36 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca); 38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
39
40 if (nsp->IsExtractedType()) {
41 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
42 } else {
43 if (title_id == 0)
44 return;
45
46 secondary_loader = std::make_unique<AppLoader_NCA>(
47 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
48 }
37} 49}
38 50
39AppLoader_NSP::~AppLoader_NSP() = default; 51AppLoader_NSP::~AppLoader_NSP() = default;
@@ -65,39 +77,58 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
65 return ResultStatus::ErrorAlreadyLoaded; 77 return ResultStatus::ErrorAlreadyLoaded;
66 } 78 }
67 79
68 if (nsp->IsExtractedType()) { 80 if (title_id == 0)
69 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); 81 return ResultStatus::ErrorNSPMissingProgramNCA;
70 } else {
71 if (title_id == 0)
72 return ResultStatus::ErrorNSPMissingProgramNCA;
73
74 secondary_loader = std::make_unique<AppLoader_NCA>(
75 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
76 82
77 if (nsp->GetStatus() != ResultStatus::Success) 83 if (nsp->GetStatus() != ResultStatus::Success)
78 return nsp->GetStatus(); 84 return nsp->GetStatus();
79 85
80 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) 86 if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
81 return nsp->GetProgramStatus(title_id); 87 return nsp->GetProgramStatus(title_id);
82 88
83 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { 89 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
84 if (!Core::Crypto::KeyManager::KeyFileExists(false)) 90 if (!Core::Crypto::KeyManager::KeyFileExists(false))
85 return ResultStatus::ErrorMissingProductionKeyFile; 91 return ResultStatus::ErrorMissingProductionKeyFile;
86 return ResultStatus::ErrorNSPMissingProgramNCA; 92 return ResultStatus::ErrorNSPMissingProgramNCA;
87 }
88 } 93 }
89 94
90 const auto result = secondary_loader->Load(process); 95 const auto result = secondary_loader->Load(process);
91 if (result != ResultStatus::Success) 96 if (result != ResultStatus::Success)
92 return result; 97 return result;
93 98
99 FileSys::VirtualFile update_raw;
100 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
101 Service::FileSystem::SetPackedUpdate(std::move(update_raw));
102
94 is_loaded = true; 103 is_loaded = true;
95 104
96 return ResultStatus::Success; 105 return ResultStatus::Success;
97} 106}
98 107
99ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) { 108ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
100 return secondary_loader->ReadRomFS(dir); 109 return secondary_loader->ReadRomFS(file);
110}
111
112u64 AppLoader_NSP::ReadRomFSIVFCOffset() const {
113 return secondary_loader->ReadRomFSIVFCOffset();
114}
115
116ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& file) {
117 if (nsp->IsExtractedType())
118 return ResultStatus::ErrorNoPackedUpdate;
119
120 const auto read =
121 nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program);
122
123 if (read == nullptr)
124 return ResultStatus::ErrorNoPackedUpdate;
125 const auto nca_test = std::make_shared<FileSys::NCA>(read);
126
127 if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS)
128 return nca_test->GetStatus();
129
130 file = read;
131 return ResultStatus::Success;
101} 132}
102 133
103ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { 134ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index eac9b819a..db91cd01e 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -37,7 +37,9 @@ public:
37 37
38 ResultStatus Load(Kernel::Process& process) override; 38 ResultStatus Load(Kernel::Process& process) override;
39 39
40 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override;
42 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
41 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 44 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadTitle(std::string& title) override; 45 ResultStatus ReadTitle(std::string& title) override;
@@ -47,7 +49,7 @@ private:
47 std::unique_ptr<AppLoader> secondary_loader; 49 std::unique_ptr<AppLoader> secondary_loader;
48 50
49 FileSys::VirtualFile icon_file; 51 FileSys::VirtualFile icon_file;
50 std::shared_ptr<FileSys::NACP> nacp_file; 52 std::unique_ptr<FileSys::NACP> nacp_file;
51 u64 title_id; 53 u64 title_id;
52}; 54};
53 55
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index eda67a8c8..461607c95 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -9,7 +9,11 @@
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h" 10#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/patch_manager.h" 11#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h"
13#include "core/file_sys/romfs.h"
14#include "core/file_sys/submission_package.h"
12#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
16#include "core/hle/service/filesystem/filesystem.h"
13#include "core/loader/nca.h" 17#include "core/loader/nca.h"
14#include "core/loader/xci.h" 18#include "core/loader/xci.h"
15 19
@@ -26,7 +30,7 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
26 return; 30 return;
27 31
28 std::tie(nacp_file, icon_file) = 32 std::tie(nacp_file, icon_file) =
29 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca); 33 FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca);
30} 34}
31 35
32AppLoader_XCI::~AppLoader_XCI() = default; 36AppLoader_XCI::~AppLoader_XCI() = default;
@@ -55,21 +59,48 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
55 if (xci->GetProgramNCAStatus() != ResultStatus::Success) 59 if (xci->GetProgramNCAStatus() != ResultStatus::Success)
56 return xci->GetProgramNCAStatus(); 60 return xci->GetProgramNCAStatus();
57 61
58 const auto nca = xci->GetProgramNCA(); 62 if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
59 if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
60 return ResultStatus::ErrorMissingProductionKeyFile; 63 return ResultStatus::ErrorMissingProductionKeyFile;
61 64
62 const auto result = nca_loader->Load(process); 65 const auto result = nca_loader->Load(process);
63 if (result != ResultStatus::Success) 66 if (result != ResultStatus::Success)
64 return result; 67 return result;
65 68
69 FileSys::VirtualFile update_raw;
70 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
71 Service::FileSystem::SetPackedUpdate(std::move(update_raw));
72
66 is_loaded = true; 73 is_loaded = true;
67 74
68 return ResultStatus::Success; 75 return ResultStatus::Success;
69} 76}
70 77
71ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& dir) { 78ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
72 return nca_loader->ReadRomFS(dir); 79 return nca_loader->ReadRomFS(file);
80}
81
82u64 AppLoader_XCI::ReadRomFSIVFCOffset() const {
83 return nca_loader->ReadRomFSIVFCOffset();
84}
85
86ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& file) {
87 u64 program_id{};
88 nca_loader->ReadProgramId(program_id);
89 if (program_id == 0)
90 return ResultStatus::ErrorXCIMissingProgramNCA;
91
92 const auto read = xci->GetSecurePartitionNSP()->GetNCAFile(
93 FileSys::GetUpdateTitleID(program_id), FileSys::ContentRecordType::Program);
94
95 if (read == nullptr)
96 return ResultStatus::ErrorNoPackedUpdate;
97 const auto nca_test = std::make_shared<FileSys::NCA>(read);
98
99 if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS)
100 return nca_test->GetStatus();
101
102 file = read;
103 return ResultStatus::Success;
73} 104}
74 105
75ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { 106ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 17e47b658..46f8dfc9e 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -37,7 +37,9 @@ public:
37 37
38 ResultStatus Load(Kernel::Process& process) override; 38 ResultStatus Load(Kernel::Process& process) override;
39 39
40 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override;
42 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
41 ResultStatus ReadProgramId(u64& out_program_id) override; 43 ResultStatus ReadProgramId(u64& out_program_id) override;
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 44 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadTitle(std::string& title) override; 45 ResultStatus ReadTitle(std::string& title) override;
@@ -47,7 +49,7 @@ private:
47 std::unique_ptr<AppLoader_NCA> nca_loader; 49 std::unique_ptr<AppLoader_NCA> nca_loader;
48 50
49 FileSys::VirtualFile icon_file; 51 FileSys::VirtualFile icon_file;
50 std::shared_ptr<FileSys::NACP> nacp_file; 52 std::unique_ptr<FileSys::NACP> nacp_file;
51}; 53};
52 54
53} // namespace Loader 55} // namespace Loader
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 014298ed6..70abd856a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -4,9 +4,9 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include <optional>
7#include <utility> 8#include <utility>
8 9
9#include <boost/optional.hpp>
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h
index 0269c7ff1..940777107 100644
--- a/src/core/memory_hook.h
+++ b/src/core/memory_hook.h
@@ -5,7 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <boost/optional.hpp> 8#include <optional>
9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
11namespace Memory { 12namespace Memory {
@@ -18,19 +19,19 @@ namespace Memory {
18 * 19 *
19 * A hook may be mapped to multiple regions of memory. 20 * A hook may be mapped to multiple regions of memory.
20 * 21 *
21 * If a boost::none or false is returned from a function, the read/write request is passed through 22 * If a std::nullopt or false is returned from a function, the read/write request is passed through
22 * to the underlying memory region. 23 * to the underlying memory region.
23 */ 24 */
24class MemoryHook { 25class MemoryHook {
25public: 26public:
26 virtual ~MemoryHook(); 27 virtual ~MemoryHook();
27 28
28 virtual boost::optional<bool> IsValidAddress(VAddr addr) = 0; 29 virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
29 30
30 virtual boost::optional<u8> Read8(VAddr addr) = 0; 31 virtual std::optional<u8> Read8(VAddr addr) = 0;
31 virtual boost::optional<u16> Read16(VAddr addr) = 0; 32 virtual std::optional<u16> Read16(VAddr addr) = 0;
32 virtual boost::optional<u32> Read32(VAddr addr) = 0; 33 virtual std::optional<u32> Read32(VAddr addr) = 0;
33 virtual boost::optional<u64> Read64(VAddr addr) = 0; 34 virtual std::optional<u64> Read64(VAddr addr) = 0;
34 35
35 virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; 36 virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
36 37
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 7d95816fe..c716a462b 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() {
74} 74}
75 75
76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { 76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
77 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
78 // values increase the time needed to recover and limit framerate again after spikes.
79 constexpr microseconds MAX_LAG_TIME_US = 25000us;
80
81 if (!Settings::values.use_frame_limit) { 77 if (!Settings::values.use_frame_limit) {
82 return; 78 return;
83 } 79 }
diff --git a/src/core/settings.h b/src/core/settings.h
index 1808f5937..b5aeff29b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -113,7 +113,8 @@ static const std::array<const char*, NumAnalogs> mapping = {{
113struct Values { 113struct Values {
114 // System 114 // System
115 bool use_docked_mode; 115 bool use_docked_mode;
116 std::string username; 116 bool enable_nfc;
117 int current_user;
117 int language_index; 118 int language_index;
118 119
119 // Controls 120 // Controls
@@ -136,7 +137,7 @@ struct Values {
136 float resolution_factor; 137 float resolution_factor;
137 bool use_frame_limit; 138 bool use_frame_limit;
138 u16 frame_limit; 139 u16 frame_limit;
139 bool use_accurate_framebuffers; 140 bool use_accurate_gpu_emulation;
140 141
141 float bg_red; 142 float bg_red;
142 float bg_green; 143 float bg_green;
@@ -155,6 +156,7 @@ struct Values {
155 // Debugging 156 // Debugging
156 bool use_gdbstub; 157 bool use_gdbstub;
157 u16 gdbstub_port; 158 u16 gdbstub_port;
159 std::string program_args;
158 160
159 // WebService 161 // WebService
160 bool enable_telemetry; 162 bool enable_telemetry;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index f29fff1e7..a3b08c740 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -2,12 +2,16 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6
7#include <mbedtls/ctr_drbg.h>
8#include <mbedtls/entropy.h>
9
5#include "common/assert.h" 10#include "common/assert.h"
6#include "common/common_types.h" 11#include "common/common_types.h"
7#include "common/file_util.h" 12#include "common/file_util.h"
13#include "common/logging/log.h"
8 14
9#include <mbedtls/ctr_drbg.h>
10#include <mbedtls/entropy.h>
11#include "core/core.h" 15#include "core/core.h"
12#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
13#include "core/file_sys/patch_manager.h" 17#include "core/file_sys/patch_manager.h"
@@ -28,11 +32,11 @@ static u64 GenerateTelemetryId() {
28 mbedtls_entropy_context entropy; 32 mbedtls_entropy_context entropy;
29 mbedtls_entropy_init(&entropy); 33 mbedtls_entropy_init(&entropy);
30 mbedtls_ctr_drbg_context ctr_drbg; 34 mbedtls_ctr_drbg_context ctr_drbg;
31 std::string personalization = "yuzu Telemetry ID"; 35 constexpr std::array<char, 18> personalization{{"yuzu Telemetry ID"}};
32 36
33 mbedtls_ctr_drbg_init(&ctr_drbg); 37 mbedtls_ctr_drbg_init(&ctr_drbg);
34 ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, 38 ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
35 reinterpret_cast<const unsigned char*>(personalization.c_str()), 39 reinterpret_cast<const unsigned char*>(personalization.data()),
36 personalization.size()) == 0); 40 personalization.size()) == 0);
37 ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id), 41 ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id),
38 sizeof(u64)) == 0); 42 sizeof(u64)) == 0);
@@ -159,8 +163,8 @@ TelemetrySession::TelemetrySession() {
159 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit", 163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",
160 Settings::values.use_frame_limit); 164 Settings::values.use_frame_limit);
161 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); 165 AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit);
162 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", 166 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
163 Settings::values.use_accurate_framebuffers); 167 Settings::values.use_accurate_gpu_emulation);
164 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 168 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
165 Settings::values.use_docked_mode); 169 Settings::values.use_docked_mode);
166} 170}
@@ -180,4 +184,13 @@ TelemetrySession::~TelemetrySession() {
180 backend = nullptr; 184 backend = nullptr;
181} 185}
182 186
187bool TelemetrySession::SubmitTestcase() {
188#ifdef ENABLE_WEB_SERVICE
189 field_collection.Accept(*backend);
190 return backend->SubmitTestcase();
191#else
192 return false;
193#endif
194}
195
183} // namespace Core 196} // namespace Core
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index cec271df0..023612b79 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
8#include "common/telemetry.h" 9#include "common/telemetry.h"
9 10
10namespace Core { 11namespace Core {
@@ -30,7 +31,11 @@ public:
30 field_collection.AddField(type, name, std::move(value)); 31 field_collection.AddField(type, name, std::move(value));
31 } 32 }
32 33
33 static void FinalizeAsyncJob(); 34 /**
35 * Submits a Testcase.
36 * @returns A bool indicating whether the submission succeeded
37 */
38 bool SubmitTestcase();
34 39
35private: 40private:
36 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session 41 Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
@@ -53,7 +58,6 @@ u64 RegenerateTelemetryId();
53 * Verifies the username and token. 58 * Verifies the username and token.
54 * @param username yuzu username to use for authentication. 59 * @param username yuzu username to use for authentication.
55 * @param token yuzu token to use for authentication. 60 * @param token yuzu token to use for authentication.
56 * @param func A function that gets exectued when the verification is finished
57 * @returns Future with bool indicating whether the verification succeeded 61 * @returns Future with bool indicating whether the verification succeeded
58 */ 62 */
59bool VerifyLogin(const std::string& username, const std::string& token); 63bool VerifyLogin(const std::string& username, const std::string& token);
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index c0a57e71f..9b8a44fa1 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -15,7 +15,8 @@ namespace ArmTests {
15TestEnvironment::TestEnvironment(bool mutable_memory_) 15TestEnvironment::TestEnvironment(bool mutable_memory_)
16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { 16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
17 17
18 Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); 18 auto process = Kernel::Process::Create(kernel, "");
19 kernel.MakeCurrentProcess(process.get());
19 page_table = &Core::CurrentProcess()->VMManager().page_table; 20 page_table = &Core::CurrentProcess()->VMManager().page_table;
20 21
21 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
@@ -63,11 +64,11 @@ void TestEnvironment::ClearWriteRecords() {
63 64
64TestEnvironment::TestMemory::~TestMemory() {} 65TestEnvironment::TestMemory::~TestMemory() {}
65 66
66boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) { 67std::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
67 return true; 68 return true;
68} 69}
69 70
70boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { 71std::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
71 const auto iter = data.find(addr); 72 const auto iter = data.find(addr);
72 73
73 if (iter == data.end()) { 74 if (iter == data.end()) {
@@ -78,15 +79,15 @@ boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
78 return iter->second; 79 return iter->second;
79} 80}
80 81
81boost::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) { 82std::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) {
82 return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8; 83 return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8;
83} 84}
84 85
85boost::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) { 86std::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) {
86 return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16; 87 return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16;
87} 88}
88 89
89boost::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { 90std::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) {
90 return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; 91 return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32;
91} 92}
92 93
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index 5de8dab4e..0b7539601 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -64,12 +64,12 @@ private:
64 64
65 ~TestMemory() override; 65 ~TestMemory() override;
66 66
67 boost::optional<bool> IsValidAddress(VAddr addr) override; 67 std::optional<bool> IsValidAddress(VAddr addr) override;
68 68
69 boost::optional<u8> Read8(VAddr addr) override; 69 std::optional<u8> Read8(VAddr addr) override;
70 boost::optional<u16> Read16(VAddr addr) override; 70 std::optional<u16> Read16(VAddr addr) override;
71 boost::optional<u32> Read32(VAddr addr) override; 71 std::optional<u32> Read32(VAddr addr) override;
72 boost::optional<u64> Read64(VAddr addr) override; 72 std::optional<u64> Read64(VAddr addr) override;
73 73
74 bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override; 74 bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override;
75 75
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 09ecc5bad..ddb1a1d69 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -51,6 +51,10 @@ add_library(video_core STATIC
51 renderer_opengl/maxwell_to_gl.h 51 renderer_opengl/maxwell_to_gl.h
52 renderer_opengl/renderer_opengl.cpp 52 renderer_opengl/renderer_opengl.cpp
53 renderer_opengl/renderer_opengl.h 53 renderer_opengl/renderer_opengl.h
54 renderer_opengl/utils.cpp
55 renderer_opengl/utils.h
56 surface.cpp
57 surface.h
54 textures/astc.cpp 58 textures/astc.cpp
55 textures/astc.h 59 textures/astc.h
56 textures/decoders.cpp 60 textures/decoders.cpp
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index f1aa6091b..28e8c13aa 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -81,7 +81,7 @@ void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) {
81 for (auto entry : commands) { 81 for (auto entry : commands) {
82 Tegra::GPUVAddr address = entry.Address(); 82 Tegra::GPUVAddr address = entry.Address();
83 u32 size = entry.sz; 83 u32 size = entry.sz;
84 const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); 84 const std::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address);
85 VAddr current_addr = *head_address; 85 VAddr current_addr = *head_address;
86 while (current_addr < *head_address + size * sizeof(CommandHeader)) { 86 while (current_addr < *head_address + size * sizeof(CommandHeader)) {
87 const CommandHeader header = {Memory::Read32(current_addr)}; 87 const CommandHeader header = {Memory::Read32(current_addr)};
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index ea1555c5d..74e44c7fe 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -4,11 +4,13 @@
4 4
5#include "core/memory.h" 5#include "core/memory.h"
6#include "video_core/engines/fermi_2d.h" 6#include "video_core/engines/fermi_2d.h"
7#include "video_core/rasterizer_interface.h"
7#include "video_core/textures/decoders.h" 8#include "video_core/textures/decoders.h"
8 9
9namespace Tegra::Engines { 10namespace Tegra::Engines {
10 11
11Fermi2D::Fermi2D(MemoryManager& memory_manager) : memory_manager(memory_manager) {} 12Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
13 : memory_manager(memory_manager), rasterizer{rasterizer} {}
12 14
13void Fermi2D::WriteReg(u32 method, u32 value) { 15void Fermi2D::WriteReg(u32 method, u32 value) {
14 ASSERT_MSG(method < Regs::NUM_REGS, 16 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -44,27 +46,36 @@ void Fermi2D::HandleSurfaceCopy() {
44 u32 src_bytes_per_pixel = RenderTargetBytesPerPixel(regs.src.format); 46 u32 src_bytes_per_pixel = RenderTargetBytesPerPixel(regs.src.format);
45 u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format); 47 u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
46 48
47 if (regs.src.linear == regs.dst.linear) { 49 if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
48 // If the input layout and the output layout are the same, just perform a raw copy. 50 rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
49 ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight()); 51 // We have to invalidate the destination region to evict any outdated surfaces from the
50 Memory::CopyBlock(dest_cpu, source_cpu, 52 // cache. We do this before actually writing the new data because the destination address
51 src_bytes_per_pixel * regs.dst.width * regs.dst.height); 53 // might contain a dirty surface that will have to be written back to memory.
52 return; 54 rasterizer.InvalidateRegion(dest_cpu,
53 } 55 dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
54 56
55 u8* src_buffer = Memory::GetPointer(source_cpu); 57 if (regs.src.linear == regs.dst.linear) {
56 u8* dst_buffer = Memory::GetPointer(dest_cpu); 58 // If the input layout and the output layout are the same, just perform a raw copy.
57 59 ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
58 if (!regs.src.linear && regs.dst.linear) { 60 Memory::CopyBlock(dest_cpu, source_cpu,
59 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 61 src_bytes_per_pixel * regs.dst.width * regs.dst.height);
60 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 62 return;
61 dst_bytes_per_pixel, src_buffer, dst_buffer, true, 63 }
62 regs.src.BlockHeight()); 64 u8* src_buffer = Memory::GetPointer(source_cpu);
63 } else { 65 u8* dst_buffer = Memory::GetPointer(dest_cpu);
64 // If the input is linear and the output is tiled, swizzle the input and copy it over. 66 if (!regs.src.linear && regs.dst.linear) {
65 Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, 67 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
66 dst_bytes_per_pixel, dst_buffer, src_buffer, false, 68 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
67 regs.dst.BlockHeight()); 69 src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer,
70 dst_buffer, true, regs.src.BlockHeight(),
71 regs.src.BlockDepth());
72 } else {
73 // If the input is linear and the output is tiled, swizzle the input and copy it over.
74 Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth,
75 src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer,
76 src_buffer, false, regs.dst.BlockHeight(),
77 regs.dst.BlockDepth());
78 }
68 } 79 }
69} 80}
70 81
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 021b83eaa..2a6e8bbbb 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -12,6 +12,10 @@
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
14 14
15namespace VideoCore {
16class RasterizerInterface;
17}
18
15namespace Tegra::Engines { 19namespace Tegra::Engines {
16 20
17#define FERMI2D_REG_INDEX(field_name) \ 21#define FERMI2D_REG_INDEX(field_name) \
@@ -19,7 +23,7 @@ namespace Tegra::Engines {
19 23
20class Fermi2D final { 24class Fermi2D final {
21public: 25public:
22 explicit Fermi2D(MemoryManager& memory_manager); 26 explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
23 ~Fermi2D() = default; 27 ~Fermi2D() = default;
24 28
25 /// Write the value to the register identified by method. 29 /// Write the value to the register identified by method.
@@ -32,9 +36,9 @@ public:
32 RenderTargetFormat format; 36 RenderTargetFormat format;
33 BitField<0, 1, u32> linear; 37 BitField<0, 1, u32> linear;
34 union { 38 union {
35 BitField<0, 4, u32> block_depth; 39 BitField<0, 4, u32> block_width;
36 BitField<4, 4, u32> block_height; 40 BitField<4, 4, u32> block_height;
37 BitField<8, 4, u32> block_width; 41 BitField<8, 4, u32> block_depth;
38 }; 42 };
39 u32 depth; 43 u32 depth;
40 u32 layer; 44 u32 layer;
@@ -49,10 +53,20 @@ public:
49 address_low); 53 address_low);
50 } 54 }
51 55
56 u32 BlockWidth() const {
57 // The block width is stored in log2 format.
58 return 1 << block_width;
59 }
60
52 u32 BlockHeight() const { 61 u32 BlockHeight() const {
53 // The block height is stored in log2 format. 62 // The block height is stored in log2 format.
54 return 1 << block_height; 63 return 1 << block_height;
55 } 64 }
65
66 u32 BlockDepth() const {
67 // The block depth is stored in log2 format.
68 return 1 << block_depth;
69 }
56 }; 70 };
57 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size"); 71 static_assert(sizeof(Surface) == 0x28, "Surface has incorrect size");
58 72
@@ -94,6 +108,8 @@ public:
94 MemoryManager& memory_manager; 108 MemoryManager& memory_manager;
95 109
96private: 110private:
111 VideoCore::RasterizerInterface& rasterizer;
112
97 /// Performs the copy from the source surface to the destination surface as configured in the 113 /// Performs the copy from the source surface to the destination surface as configured in the
98 /// registers. 114 /// registers.
99 void HandleSurfaceCopy(); 115 void HandleSurfaceCopy();
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index 66ae6332d..585290d9f 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -5,10 +5,14 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/memory.h" 6#include "core/memory.h"
7#include "video_core/engines/kepler_memory.h" 7#include "video_core/engines/kepler_memory.h"
8#include "video_core/rasterizer_interface.h"
8 9
9namespace Tegra::Engines { 10namespace Tegra::Engines {
10 11
11KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {} 12KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer,
13 MemoryManager& memory_manager)
14 : memory_manager(memory_manager), rasterizer{rasterizer} {}
15
12KeplerMemory::~KeplerMemory() = default; 16KeplerMemory::~KeplerMemory() = default;
13 17
14void KeplerMemory::WriteReg(u32 method, u32 value) { 18void KeplerMemory::WriteReg(u32 method, u32 value) {
@@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) {
37 VAddr dest_address = 41 VAddr dest_address =
38 *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); 42 *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
39 43
44 // We have to invalidate the destination region to evict any outdated surfaces from the cache.
45 // We do this before actually writing the new data because the destination address might contain
46 // a dirty surface that will have to be written back to memory.
47 rasterizer.InvalidateRegion(dest_address, sizeof(u32));
48
40 Memory::Write32(dest_address, data); 49 Memory::Write32(dest_address, data);
41 50
42 state.write_offset++; 51 state.write_offset++;
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index b0d0078cf..bf4a13cff 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -11,6 +11,10 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/memory_manager.h" 12#include "video_core/memory_manager.h"
13 13
14namespace VideoCore {
15class RasterizerInterface;
16}
17
14namespace Tegra::Engines { 18namespace Tegra::Engines {
15 19
16#define KEPLERMEMORY_REG_INDEX(field_name) \ 20#define KEPLERMEMORY_REG_INDEX(field_name) \
@@ -18,7 +22,7 @@ namespace Tegra::Engines {
18 22
19class KeplerMemory final { 23class KeplerMemory final {
20public: 24public:
21 KeplerMemory(MemoryManager& memory_manager); 25 KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
22 ~KeplerMemory(); 26 ~KeplerMemory();
23 27
24 /// Write the value to the register identified by method. 28 /// Write the value to the register identified by method.
@@ -72,6 +76,7 @@ public:
72 76
73private: 77private:
74 MemoryManager& memory_manager; 78 MemoryManager& memory_manager;
79 VideoCore::RasterizerInterface& rasterizer;
75 80
76 void ProcessData(u32 data); 81 void ProcessData(u32 data);
77}; 82};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 8afd26fe9..d79c50919 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include <cstring>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/core_timing.h" 9#include "core/core_timing.h"
@@ -13,28 +14,46 @@
13#include "video_core/renderer_base.h" 14#include "video_core/renderer_base.h"
14#include "video_core/textures/texture.h" 15#include "video_core/textures/texture.h"
15 16
16namespace Tegra { 17namespace Tegra::Engines {
17namespace Engines {
18 18
19/// First register id that is actually a Macro call. 19/// First register id that is actually a Macro call.
20constexpr u32 MacroRegistersStart = 0xE00; 20constexpr u32 MacroRegistersStart = 0xE00;
21 21
22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) 22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} 23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {
24 InitializeRegisterDefaults();
25}
26
27void Maxwell3D::InitializeRegisterDefaults() {
28 // Initializes registers to their default values - what games expect them to be at boot. This is
29 // for certain registers that may not be explicitly set by games.
30
31 // Reset all registers to zero
32 std::memset(&regs, 0, sizeof(regs));
33
34 // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is
35 // needed for ARMS.
36 for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) {
37 regs.viewport[viewport].depth_range_near = 0.0f;
38 regs.viewport[viewport].depth_range_far = 1.0f;
39 }
40}
24 41
25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 42void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
26 // Reset the current macro. 43 // Reset the current macro.
27 executing_macro = 0; 44 executing_macro = 0;
28 45
29 // The requested macro must have been uploaded already. 46 // Lookup the macro offset
30 auto macro_code = uploaded_macros.find(method); 47 const u32 entry{(method - MacroRegistersStart) >> 1};
31 if (macro_code == uploaded_macros.end()) { 48 const auto& search{macro_offsets.find(entry)};
32 LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method); 49 if (search == macro_offsets.end()) {
50 LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method);
51 UNREACHABLE();
33 return; 52 return;
34 } 53 }
35 54
36 // Execute the current macro. 55 // Execute the current macro.
37 macro_interpreter.Execute(macro_code->second, std::move(parameters)); 56 macro_interpreter.Execute(search->second, std::move(parameters));
38} 57}
39 58
40void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) { 59void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
@@ -80,6 +99,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
80 ProcessMacroUpload(value); 99 ProcessMacroUpload(value);
81 break; 100 break;
82 } 101 }
102 case MAXWELL3D_REG_INDEX(macros.bind): {
103 ProcessMacroBind(value);
104 break;
105 }
83 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]): 106 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
84 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]): 107 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
85 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]): 108 case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
@@ -141,22 +164,25 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
141} 164}
142 165
143void Maxwell3D::ProcessMacroUpload(u32 data) { 166void Maxwell3D::ProcessMacroUpload(u32 data) {
144 // Store the uploaded macro code to interpret them when they're called. 167 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
145 auto& macro = uploaded_macros[regs.macros.entry * 2 + MacroRegistersStart]; 168 "upload_address exceeded macro_memory size!");
146 macro.push_back(data); 169 macro_memory[regs.macros.upload_address++] = data;
170}
171
172void Maxwell3D::ProcessMacroBind(u32 data) {
173 macro_offsets[regs.macros.entry] = data;
147} 174}
148 175
149void Maxwell3D::ProcessQueryGet() { 176void Maxwell3D::ProcessQueryGet() {
150 GPUVAddr sequence_address = regs.query.QueryAddress(); 177 GPUVAddr sequence_address = regs.query.QueryAddress();
151 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application 178 // Since the sequence address is given as a GPU VAddr, we have to convert it to an application
152 // VAddr before writing. 179 // VAddr before writing.
153 boost::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address); 180 std::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address);
154 181
155 // TODO(Subv): Support the other query units. 182 // TODO(Subv): Support the other query units.
156 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, 183 ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop,
157 "Units other than CROP are unimplemented"); 184 "Units other than CROP are unimplemented");
158 185
159 u32 value = Memory::Read32(*address);
160 u64 result = 0; 186 u64 result = 0;
161 187
162 // TODO(Subv): Support the other query variables 188 // TODO(Subv): Support the other query variables
@@ -269,7 +295,7 @@ void Maxwell3D::ProcessCBData(u32 value) {
269 // Don't allow writing past the end of the buffer. 295 // Don't allow writing past the end of the buffer.
270 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size); 296 ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size);
271 297
272 boost::optional<VAddr> address = 298 std::optional<VAddr> address =
273 memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); 299 memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos);
274 300
275 Memory::Write32(*address, value); 301 Memory::Write32(*address, value);
@@ -282,7 +308,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
282 GPUVAddr tic_base_address = regs.tic.TICAddress(); 308 GPUVAddr tic_base_address = regs.tic.TICAddress();
283 309
284 GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry); 310 GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry);
285 boost::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu); 311 std::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu);
286 312
287 Texture::TICEntry tic_entry; 313 Texture::TICEntry tic_entry;
288 Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); 314 Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry));
@@ -306,7 +332,7 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const {
306 GPUVAddr tsc_base_address = regs.tsc.TSCAddress(); 332 GPUVAddr tsc_base_address = regs.tsc.TSCAddress();
307 333
308 GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry); 334 GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry);
309 boost::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu); 335 std::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu);
310 336
311 Texture::TSCEntry tsc_entry; 337 Texture::TSCEntry tsc_entry;
312 Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry)); 338 Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry));
@@ -370,7 +396,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage,
370 396
371 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); 397 ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size);
372 398
373 boost::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); 399 std::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address);
374 Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)}; 400 Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)};
375 401
376 Texture::FullTextureInfo tex_info{}; 402 Texture::FullTextureInfo tex_info{};
@@ -408,5 +434,4 @@ void Maxwell3D::ProcessClearBuffers() {
408 rasterizer.Clear(); 434 rasterizer.Clear();
409} 435}
410 436
411} // namespace Engines 437} // namespace Tegra::Engines
412} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 4290da33f..50873813e 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -347,6 +347,16 @@ public:
347 DecrWrap = 8, 347 DecrWrap = 8,
348 }; 348 };
349 349
350 enum class MemoryLayout : u32 {
351 Linear = 0,
352 BlockLinear = 1,
353 };
354
355 enum class InvMemoryLayout : u32 {
356 BlockLinear = 0,
357 Linear = 1,
358 };
359
350 struct Cull { 360 struct Cull {
351 enum class FrontFace : u32 { 361 enum class FrontFace : u32 {
352 ClockWise = 0x0900, 362 ClockWise = 0x0900,
@@ -432,8 +442,16 @@ public:
432 u32 width; 442 u32 width;
433 u32 height; 443 u32 height;
434 Tegra::RenderTargetFormat format; 444 Tegra::RenderTargetFormat format;
435 u32 block_dimensions; 445 union {
436 u32 array_mode; 446 BitField<0, 3, u32> block_width;
447 BitField<4, 3, u32> block_height;
448 BitField<8, 3, u32> block_depth;
449 BitField<12, 1, InvMemoryLayout> type;
450 } memory_layout;
451 union {
452 BitField<0, 16, u32> array_mode;
453 BitField<16, 1, u32> volume;
454 };
437 u32 layer_stride; 455 u32 layer_stride;
438 u32 base_layer; 456 u32 base_layer;
439 INSERT_PADDING_WORDS(7); 457 INSERT_PADDING_WORDS(7);
@@ -457,12 +475,13 @@ public:
457 INSERT_PADDING_WORDS(0x45); 475 INSERT_PADDING_WORDS(0x45);
458 476
459 struct { 477 struct {
460 INSERT_PADDING_WORDS(1); 478 u32 upload_address;
461 u32 data; 479 u32 data;
462 u32 entry; 480 u32 entry;
481 u32 bind;
463 } macros; 482 } macros;
464 483
465 INSERT_PADDING_WORDS(0x189); 484 INSERT_PADDING_WORDS(0x188);
466 485
467 u32 tfb_enabled; 486 u32 tfb_enabled;
468 487
@@ -532,7 +551,21 @@ public:
532 INSERT_PADDING_WORDS(0x3); 551 INSERT_PADDING_WORDS(0x3);
533 s32 clear_stencil; 552 s32 clear_stencil;
534 553
535 INSERT_PADDING_WORDS(0x6C); 554 INSERT_PADDING_WORDS(0x17);
555
556 struct {
557 u32 enable;
558 union {
559 BitField<0, 16, u32> min_x;
560 BitField<16, 16, u32> max_x;
561 };
562 union {
563 BitField<0, 16, u32> min_y;
564 BitField<16, 16, u32> max_y;
565 };
566 } scissor_test;
567
568 INSERT_PADDING_WORDS(0x52);
536 569
537 s32 stencil_back_func_ref; 570 s32 stencil_back_func_ref;
538 u32 stencil_back_mask; 571 u32 stencil_back_mask;
@@ -548,7 +581,12 @@ public:
548 u32 address_high; 581 u32 address_high;
549 u32 address_low; 582 u32 address_low;
550 Tegra::DepthFormat format; 583 Tegra::DepthFormat format;
551 u32 block_dimensions; 584 union {
585 BitField<0, 4, u32> block_width;
586 BitField<4, 4, u32> block_height;
587 BitField<8, 4, u32> block_depth;
588 BitField<20, 1, InvMemoryLayout> type;
589 } memory_layout;
552 u32 layer_stride; 590 u32 layer_stride;
553 591
554 GPUVAddr Address() const { 592 GPUVAddr Address() const {
@@ -606,8 +644,10 @@ public:
606 u32 d3d_cull_mode; 644 u32 d3d_cull_mode;
607 645
608 ComparisonOp depth_test_func; 646 ComparisonOp depth_test_func;
647 float alpha_test_ref;
648 ComparisonOp alpha_test_func;
609 649
610 INSERT_PADDING_WORDS(0xB); 650 INSERT_PADDING_WORDS(0x9);
611 651
612 struct { 652 struct {
613 u32 separate_alpha; 653 u32 separate_alpha;
@@ -684,7 +724,11 @@ public:
684 StencilOp stencil_back_op_zpass; 724 StencilOp stencil_back_op_zpass;
685 ComparisonOp stencil_back_func_func; 725 ComparisonOp stencil_back_func_func;
686 726
687 INSERT_PADDING_WORDS(0x17); 727 INSERT_PADDING_WORDS(0x4);
728
729 u32 framebuffer_srgb;
730
731 INSERT_PADDING_WORDS(0x12);
688 732
689 union { 733 union {
690 BitField<2, 1, u32> coord_origin; 734 BitField<2, 1, u32> coord_origin;
@@ -712,7 +756,14 @@ public:
712 }; 756 };
713 } draw; 757 } draw;
714 758
715 INSERT_PADDING_WORDS(0x6B); 759 INSERT_PADDING_WORDS(0xA);
760
761 struct {
762 u32 enabled;
763 u32 index;
764 } primitive_restart;
765
766 INSERT_PADDING_WORDS(0x5F);
716 767
717 struct { 768 struct {
718 u32 start_addr_high; 769 u32 start_addr_high;
@@ -944,10 +995,25 @@ public:
944 /// Returns the texture information for a specific texture in a specific shader stage. 995 /// Returns the texture information for a specific texture in a specific shader stage.
945 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; 996 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
946 997
998 /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
999 /// we've seen used.
1000 using MacroMemory = std::array<u32, 0x40000>;
1001
1002 /// Gets a reference to macro memory.
1003 const MacroMemory& GetMacroMemory() const {
1004 return macro_memory;
1005 }
1006
947private: 1007private:
1008 void InitializeRegisterDefaults();
1009
948 VideoCore::RasterizerInterface& rasterizer; 1010 VideoCore::RasterizerInterface& rasterizer;
949 1011
950 std::unordered_map<u32, std::vector<u32>> uploaded_macros; 1012 /// Start offsets of each macro in macro_memory
1013 std::unordered_map<u32, u32> macro_offsets;
1014
1015 /// Memory for macro code
1016 MacroMemory macro_memory;
951 1017
952 /// Macro method that is currently being executed / being fed parameters. 1018 /// Macro method that is currently being executed / being fed parameters.
953 u32 executing_macro = 0; 1019 u32 executing_macro = 0;
@@ -970,9 +1036,12 @@ private:
970 */ 1036 */
971 void CallMacroMethod(u32 method, std::vector<u32> parameters); 1037 void CallMacroMethod(u32 method, std::vector<u32> parameters);
972 1038
973 /// Handles writes to the macro uploading registers. 1039 /// Handles writes to the macro uploading register.
974 void ProcessMacroUpload(u32 data); 1040 void ProcessMacroUpload(u32 data);
975 1041
1042 /// Handles writes to the macro bind register.
1043 void ProcessMacroBind(u32 data);
1044
976 /// Handles a write to the CLEAR_BUFFERS register. 1045 /// Handles a write to the CLEAR_BUFFERS register.
977 void ProcessClearBuffers(); 1046 void ProcessClearBuffers();
978 1047
@@ -1002,6 +1071,7 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D);
1002ASSERT_REG_POSITION(clear_color[0], 0x360); 1071ASSERT_REG_POSITION(clear_color[0], 0x360);
1003ASSERT_REG_POSITION(clear_depth, 0x364); 1072ASSERT_REG_POSITION(clear_depth, 0x364);
1004ASSERT_REG_POSITION(clear_stencil, 0x368); 1073ASSERT_REG_POSITION(clear_stencil, 0x368);
1074ASSERT_REG_POSITION(scissor_test, 0x380);
1005ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); 1075ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
1006ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); 1076ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
1007ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); 1077ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
@@ -1037,9 +1107,11 @@ ASSERT_REG_POSITION(stencil_back_op_fail, 0x566);
1037ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); 1107ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567);
1038ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); 1108ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568);
1039ASSERT_REG_POSITION(stencil_back_func_func, 0x569); 1109ASSERT_REG_POSITION(stencil_back_func_func, 0x569);
1110ASSERT_REG_POSITION(framebuffer_srgb, 0x56E);
1040ASSERT_REG_POSITION(point_coord_replace, 0x581); 1111ASSERT_REG_POSITION(point_coord_replace, 0x581);
1041ASSERT_REG_POSITION(code_address, 0x582); 1112ASSERT_REG_POSITION(code_address, 0x582);
1042ASSERT_REG_POSITION(draw, 0x585); 1113ASSERT_REG_POSITION(draw, 0x585);
1114ASSERT_REG_POSITION(primitive_restart, 0x591);
1043ASSERT_REG_POSITION(index_array, 0x5F2); 1115ASSERT_REG_POSITION(index_array, 0x5F2);
1044ASSERT_REG_POSITION(instanced_arrays, 0x620); 1116ASSERT_REG_POSITION(instanced_arrays, 0x620);
1045ASSERT_REG_POSITION(cull, 0x646); 1117ASSERT_REG_POSITION(cull, 0x646);
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp
index 59e28b22d..8b5f08351 100644
--- a/src/video_core/engines/maxwell_compute.cpp
+++ b/src/video_core/engines/maxwell_compute.cpp
@@ -6,8 +6,7 @@
6#include "core/core.h" 6#include "core/core.h"
7#include "video_core/engines/maxwell_compute.h" 7#include "video_core/engines/maxwell_compute.h"
8 8
9namespace Tegra { 9namespace Tegra::Engines {
10namespace Engines {
11 10
12void MaxwellCompute::WriteReg(u32 method, u32 value) { 11void MaxwellCompute::WriteReg(u32 method, u32 value) {
13 ASSERT_MSG(method < Regs::NUM_REGS, 12 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -26,5 +25,4 @@ void MaxwellCompute::WriteReg(u32 method, u32 value) {
26 } 25 }
27} 26}
28 27
29} // namespace Engines 28} // namespace Tegra::Engines
30} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index aa7481b8c..b8a78cf82 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -4,12 +4,13 @@
4 4
5#include "core/memory.h" 5#include "core/memory.h"
6#include "video_core/engines/maxwell_dma.h" 6#include "video_core/engines/maxwell_dma.h"
7#include "video_core/rasterizer_interface.h"
7#include "video_core/textures/decoders.h" 8#include "video_core/textures/decoders.h"
8 9
9namespace Tegra { 10namespace Tegra::Engines {
10namespace Engines {
11 11
12MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {} 12MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
13 : memory_manager(memory_manager), rasterizer{rasterizer} {}
13 14
14void MaxwellDMA::WriteReg(u32 method, u32 value) { 15void MaxwellDMA::WriteReg(u32 method, u32 value) {
15 ASSERT_MSG(method < Regs::NUM_REGS, 16 ASSERT_MSG(method < Regs::NUM_REGS,
@@ -44,38 +45,77 @@ void MaxwellDMA::HandleCopy() {
44 ASSERT(regs.exec.query_mode == Regs::QueryMode::None); 45 ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
45 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None); 46 ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
46 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2); 47 ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
47 ASSERT(regs.src_params.pos_x == 0);
48 ASSERT(regs.src_params.pos_y == 0);
49 ASSERT(regs.dst_params.pos_x == 0); 48 ASSERT(regs.dst_params.pos_x == 0);
50 ASSERT(regs.dst_params.pos_y == 0); 49 ASSERT(regs.dst_params.pos_y == 0);
51 50
52 if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { 51 if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
53 std::size_t copy_size = regs.x_count; 52 // If both the source and the destination are in block layout, assert.
53 UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
54 return;
55 }
54 56
57 if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
55 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D 58 // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
56 // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). 59 // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
57 if (regs.exec.enable_2d) { 60 // y_count).
58 copy_size = copy_size * regs.y_count; 61 if (!regs.exec.enable_2d) {
62 Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count);
63 return;
59 } 64 }
60 65
61 Memory::CopyBlock(dest_cpu, source_cpu, copy_size); 66 // If both the source and the destination are in linear layout, perform a line-by-line
67 // copy. We're going to take a subrect of size (x_count, y_count) from the source
68 // rectangle. There is no need to manually flush/invalidate the regions because
69 // CopyBlock does that for us.
70 for (u32 line = 0; line < regs.y_count; ++line) {
71 const VAddr source_line = source_cpu + line * regs.src_pitch;
72 const VAddr dest_line = dest_cpu + line * regs.dst_pitch;
73 Memory::CopyBlock(dest_line, source_line, regs.x_count);
74 }
62 return; 75 return;
63 } 76 }
64 77
65 ASSERT(regs.exec.enable_2d == 1); 78 ASSERT(regs.exec.enable_2d == 1);
66 u8* src_buffer = Memory::GetPointer(source_cpu); 79
67 u8* dst_buffer = Memory::GetPointer(dest_cpu); 80 const std::size_t copy_size = regs.x_count * regs.y_count;
81
82 const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) {
83 // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
84 // copying.
85 rasterizer.FlushRegion(source_cpu, src_size);
86
87 // We have to invalidate the destination region to evict any outdated surfaces from the
88 // cache. We do this before actually writing the new data because the destination address
89 // might contain a dirty surface that will have to be written back to memory.
90 rasterizer.InvalidateRegion(dest_cpu, dst_size);
91 };
68 92
69 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { 93 if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
94 ASSERT(regs.src_params.size_z == 1);
70 // If the input is tiled and the output is linear, deswizzle the input and copy it over. 95 // If the input is tiled and the output is linear, deswizzle the input and copy it over.
71 Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer, 96
72 dst_buffer, true, regs.src_params.BlockHeight()); 97 const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
98
99 FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
100 copy_size * src_bytes_per_pixel);
101
102 Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
103 regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu,
104 regs.src_params.BlockHeight(), regs.src_params.pos_x,
105 regs.src_params.pos_y);
73 } else { 106 } else {
107 ASSERT(regs.dst_params.size_z == 1);
108 ASSERT(regs.src_pitch == regs.x_count);
109
110 const u32 src_bpp = regs.src_pitch / regs.x_count;
111
112 FlushAndInvalidate(regs.src_pitch * regs.y_count,
113 regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
114
74 // If the input is linear and the output is tiled, swizzle the input and copy it over. 115 // If the input is linear and the output is tiled, swizzle the input and copy it over.
75 Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer, 116 Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
76 src_buffer, false, regs.dst_params.BlockHeight()); 117 src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight());
77 } 118 }
78} 119}
79 120
80} // namespace Engines 121} // namespace Tegra::Engines
81} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 311ccb616..5f3704f05 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -12,11 +12,15 @@
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13#include "video_core/memory_manager.h" 13#include "video_core/memory_manager.h"
14 14
15namespace VideoCore {
16class RasterizerInterface;
17}
18
15namespace Tegra::Engines { 19namespace Tegra::Engines {
16 20
17class MaxwellDMA final { 21class MaxwellDMA final {
18public: 22public:
19 explicit MaxwellDMA(MemoryManager& memory_manager); 23 explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
20 ~MaxwellDMA() = default; 24 ~MaxwellDMA() = default;
21 25
22 /// Write the value to the register identified by method. 26 /// Write the value to the register identified by method.
@@ -43,6 +47,10 @@ public:
43 u32 BlockHeight() const { 47 u32 BlockHeight() const {
44 return 1 << block_height; 48 return 1 << block_height;
45 } 49 }
50
51 u32 BlockDepth() const {
52 return 1 << block_depth;
53 }
46 }; 54 };
47 55
48 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); 56 static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
@@ -129,6 +137,8 @@ public:
129 MemoryManager& memory_manager; 137 MemoryManager& memory_manager;
130 138
131private: 139private:
140 VideoCore::RasterizerInterface& rasterizer;
141
132 /// Performs the copy from the source buffer to the destination buffer as configured in the 142 /// Performs the copy from the source buffer to the destination buffer as configured in the
133 /// registers. 143 /// registers.
134 void HandleCopy(); 144 void HandleCopy();
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index b1f137b9c..83a6fd875 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -5,12 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <bitset> 7#include <bitset>
8#include <optional>
8#include <string> 9#include <string>
9#include <tuple> 10#include <tuple>
10#include <vector> 11#include <vector>
11 12
12#include <boost/optional.hpp>
13
14#include "common/assert.h" 13#include "common/assert.h"
15#include "common/bit_field.h" 14#include "common/bit_field.h"
16#include "common/common_types.h" 15#include "common/common_types.h"
@@ -79,6 +78,7 @@ union Attribute {
79 constexpr explicit Attribute(u64 value) : value(value) {} 78 constexpr explicit Attribute(u64 value) : value(value) {}
80 79
81 enum class Index : u64 { 80 enum class Index : u64 {
81 PointSize = 6,
82 Position = 7, 82 Position = 7,
83 Attribute_0 = 8, 83 Attribute_0 = 8,
84 Attribute_31 = 39, 84 Attribute_31 = 39,
@@ -207,6 +207,16 @@ enum class UniformType : u64 {
207 Double = 5, 207 Double = 5,
208}; 208};
209 209
210enum class StoreType : u64 {
211 Unsigned8 = 0,
212 Signed8 = 1,
213 Unsigned16 = 2,
214 Signed16 = 3,
215 Bytes32 = 4,
216 Bytes64 = 5,
217 Bytes128 = 6,
218};
219
210enum class IMinMaxExchange : u64 { 220enum class IMinMaxExchange : u64 {
211 None = 0, 221 None = 0,
212 XLo = 1, 222 XLo = 1,
@@ -214,6 +224,18 @@ enum class IMinMaxExchange : u64 {
214 XHi = 3, 224 XHi = 3,
215}; 225};
216 226
227enum class VideoType : u64 {
228 Size16_Low = 0,
229 Size16_High = 1,
230 Size32 = 2,
231 Invalid = 3,
232};
233
234enum class VmadShr : u64 {
235 Shr7 = 1,
236 Shr15 = 2,
237};
238
217enum class XmadMode : u64 { 239enum class XmadMode : u64 {
218 None = 0, 240 None = 0,
219 CLo = 1, 241 CLo = 1,
@@ -255,7 +277,7 @@ enum class ControlCode : u64 {
255 GTU = 12, 277 GTU = 12,
256 NEU = 13, 278 NEU = 13,
257 GEU = 14, 279 GEU = 14,
258 // 280 T = 15,
259 OFF = 16, 281 OFF = 16,
260 LO = 17, 282 LO = 17,
261 SFF = 18, 283 SFF = 18,
@@ -314,6 +336,35 @@ enum class TextureMiscMode : u64 {
314 PTP, 336 PTP,
315}; 337};
316 338
339enum class IsberdMode : u64 {
340 None = 0,
341 Patch = 1,
342 Prim = 2,
343 Attr = 3,
344};
345
346enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
347
348enum class HalfType : u64 {
349 H0_H1 = 0,
350 F32 = 1,
351 H0_H0 = 2,
352 H1_H1 = 3,
353};
354
355enum class HalfMerge : u64 {
356 H0_H1 = 0,
357 F32 = 1,
358 Mrg_H0 = 2,
359 Mrg_H1 = 3,
360};
361
362enum class HalfPrecision : u64 {
363 None = 0,
364 FTZ = 1,
365 FMZ = 2,
366};
367
317enum class IpaInterpMode : u64 { 368enum class IpaInterpMode : u64 {
318 Linear = 0, 369 Linear = 0,
319 Perspective = 1, 370 Perspective = 1,
@@ -340,6 +391,87 @@ struct IpaMode {
340 } 391 }
341}; 392};
342 393
394enum class SystemVariable : u64 {
395 LaneId = 0x00,
396 VirtCfg = 0x02,
397 VirtId = 0x03,
398 Pm0 = 0x04,
399 Pm1 = 0x05,
400 Pm2 = 0x06,
401 Pm3 = 0x07,
402 Pm4 = 0x08,
403 Pm5 = 0x09,
404 Pm6 = 0x0a,
405 Pm7 = 0x0b,
406 OrderingTicket = 0x0f,
407 PrimType = 0x10,
408 InvocationId = 0x11,
409 Ydirection = 0x12,
410 ThreadKill = 0x13,
411 ShaderType = 0x14,
412 DirectBeWriteAddressLow = 0x15,
413 DirectBeWriteAddressHigh = 0x16,
414 DirectBeWriteEnabled = 0x17,
415 MachineId0 = 0x18,
416 MachineId1 = 0x19,
417 MachineId2 = 0x1a,
418 MachineId3 = 0x1b,
419 Affinity = 0x1c,
420 InvocationInfo = 0x1d,
421 WscaleFactorXY = 0x1e,
422 WscaleFactorZ = 0x1f,
423 Tid = 0x20,
424 TidX = 0x21,
425 TidY = 0x22,
426 TidZ = 0x23,
427 CtaParam = 0x24,
428 CtaIdX = 0x25,
429 CtaIdY = 0x26,
430 CtaIdZ = 0x27,
431 NtId = 0x28,
432 CirQueueIncrMinusOne = 0x29,
433 Nlatc = 0x2a,
434 SmSpaVersion = 0x2c,
435 MultiPassShaderInfo = 0x2d,
436 LwinHi = 0x2e,
437 SwinHi = 0x2f,
438 SwinLo = 0x30,
439 SwinSz = 0x31,
440 SmemSz = 0x32,
441 SmemBanks = 0x33,
442 LwinLo = 0x34,
443 LwinSz = 0x35,
444 LmemLosz = 0x36,
445 LmemHioff = 0x37,
446 EqMask = 0x38,
447 LtMask = 0x39,
448 LeMask = 0x3a,
449 GtMask = 0x3b,
450 GeMask = 0x3c,
451 RegAlloc = 0x3d,
452 CtxAddr = 0x3e, // .fmask = F_SM50
453 BarrierAlloc = 0x3e, // .fmask = F_SM60
454 GlobalErrorStatus = 0x40,
455 WarpErrorStatus = 0x42,
456 WarpErrorStatusClear = 0x43,
457 PmHi0 = 0x48,
458 PmHi1 = 0x49,
459 PmHi2 = 0x4a,
460 PmHi3 = 0x4b,
461 PmHi4 = 0x4c,
462 PmHi5 = 0x4d,
463 PmHi6 = 0x4e,
464 PmHi7 = 0x4f,
465 ClockLo = 0x50,
466 ClockHi = 0x51,
467 GlobalTimerLo = 0x52,
468 GlobalTimerHi = 0x53,
469 HwTaskId = 0x60,
470 CircularQueueEntryIndex = 0x61,
471 CircularQueueEntryAddressLow = 0x62,
472 CircularQueueEntryAddressHigh = 0x63,
473};
474
343union Instruction { 475union Instruction {
344 Instruction& operator=(const Instruction& instr) { 476 Instruction& operator=(const Instruction& instr) {
345 value = instr.value; 477 value = instr.value;
@@ -362,6 +494,7 @@ union Instruction {
362 BitField<48, 16, u64> opcode; 494 BitField<48, 16, u64> opcode;
363 495
364 union { 496 union {
497 BitField<20, 16, u64> imm20_16;
365 BitField<20, 19, u64> imm20_19; 498 BitField<20, 19, u64> imm20_19;
366 BitField<20, 32, s64> imm20_32; 499 BitField<20, 32, s64> imm20_32;
367 BitField<45, 1, u64> negate_b; 500 BitField<45, 1, u64> negate_b;
@@ -403,6 +536,10 @@ union Instruction {
403 } 536 }
404 } lop3; 537 } lop3;
405 538
539 u16 GetImm20_16() const {
540 return static_cast<u16>(imm20_16);
541 }
542
406 u32 GetImm20_19() const { 543 u32 GetImm20_19() const {
407 u32 imm{static_cast<u32>(imm20_19)}; 544 u32 imm{static_cast<u32>(imm20_19)};
408 imm <<= 12; 545 imm <<= 12;
@@ -437,6 +574,14 @@ union Instruction {
437 } fmul; 574 } fmul;
438 575
439 union { 576 union {
577 BitField<55, 1, u64> saturate;
578 } fmul32;
579
580 union {
581 BitField<52, 1, u64> generates_cc;
582 } op_32;
583
584 union {
440 BitField<48, 1, u64> is_signed; 585 BitField<48, 1, u64> is_signed;
441 } shift; 586 } shift;
442 587
@@ -447,6 +592,70 @@ union Instruction {
447 } alu_integer; 592 } alu_integer;
448 593
449 union { 594 union {
595 BitField<39, 1, u64> ftz;
596 BitField<32, 1, u64> saturate;
597 BitField<49, 2, HalfMerge> merge;
598
599 BitField<43, 1, u64> negate_a;
600 BitField<44, 1, u64> abs_a;
601 BitField<47, 2, HalfType> type_a;
602
603 BitField<31, 1, u64> negate_b;
604 BitField<30, 1, u64> abs_b;
605 BitField<47, 2, HalfType> type_b;
606
607 BitField<35, 2, HalfType> type_c;
608 } alu_half;
609
610 union {
611 BitField<39, 2, HalfPrecision> precision;
612 BitField<39, 1, u64> ftz;
613 BitField<52, 1, u64> saturate;
614 BitField<49, 2, HalfMerge> merge;
615
616 BitField<43, 1, u64> negate_a;
617 BitField<44, 1, u64> abs_a;
618 BitField<47, 2, HalfType> type_a;
619 } alu_half_imm;
620
621 union {
622 BitField<29, 1, u64> first_negate;
623 BitField<20, 9, u64> first;
624
625 BitField<56, 1, u64> second_negate;
626 BitField<30, 9, u64> second;
627
628 u32 PackImmediates() const {
629 // Immediates are half floats shifted.
630 constexpr u32 imm_shift = 6;
631 return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift)));
632 }
633 } half_imm;
634
635 union {
636 union {
637 BitField<37, 2, HalfPrecision> precision;
638 BitField<32, 1, u64> saturate;
639
640 BitField<30, 1, u64> negate_c;
641 BitField<35, 2, HalfType> type_c;
642 } rr;
643
644 BitField<57, 2, HalfPrecision> precision;
645 BitField<52, 1, u64> saturate;
646
647 BitField<49, 2, HalfMerge> merge;
648
649 BitField<47, 2, HalfType> type_a;
650
651 BitField<56, 1, u64> negate_b;
652 BitField<28, 2, HalfType> type_b;
653
654 BitField<51, 1, u64> negate_c;
655 BitField<53, 2, HalfType> type_reg39;
656 } hfma2;
657
658 union {
450 BitField<40, 1, u64> invert; 659 BitField<40, 1, u64> invert;
451 } popc; 660 } popc;
452 661
@@ -552,6 +761,18 @@ union Instruction {
552 } ld_c; 761 } ld_c;
553 762
554 union { 763 union {
764 BitField<48, 3, StoreType> type;
765 } ldst_sl;
766
767 union {
768 BitField<44, 2, u64> unknown;
769 } ld_l;
770
771 union {
772 BitField<44, 2, u64> unknown;
773 } st_l;
774
775 union {
555 BitField<0, 3, u64> pred0; 776 BitField<0, 3, u64> pred0;
556 BitField<3, 3, u64> pred3; 777 BitField<3, 3, u64> pred3;
557 BitField<7, 1, u64> abs_a; 778 BitField<7, 1, u64> abs_a;
@@ -562,7 +783,6 @@ union Instruction {
562 BitField<45, 2, PredOperation> op; 783 BitField<45, 2, PredOperation> op;
563 BitField<47, 1, u64> ftz; 784 BitField<47, 1, u64> ftz;
564 BitField<48, 4, PredCondition> cond; 785 BitField<48, 4, PredCondition> cond;
565 BitField<56, 1, u64> neg_b;
566 } fsetp; 786 } fsetp;
567 787
568 union { 788 union {
@@ -589,6 +809,14 @@ union Instruction {
589 } psetp; 809 } psetp;
590 810
591 union { 811 union {
812 BitField<43, 4, PredCondition> cond;
813 BitField<45, 2, PredOperation> op;
814 BitField<3, 3, u64> pred3;
815 BitField<0, 3, u64> pred0;
816 BitField<39, 3, u64> pred39;
817 } vsetp;
818
819 union {
592 BitField<12, 3, u64> pred12; 820 BitField<12, 3, u64> pred12;
593 BitField<15, 1, u64> neg_pred12; 821 BitField<15, 1, u64> neg_pred12;
594 BitField<24, 2, PredOperation> cond; 822 BitField<24, 2, PredOperation> cond;
@@ -610,6 +838,23 @@ union Instruction {
610 } csetp; 838 } csetp;
611 839
612 union { 840 union {
841 BitField<35, 4, PredCondition> cond;
842 BitField<49, 1, u64> h_and;
843 BitField<6, 1, u64> ftz;
844 BitField<45, 2, PredOperation> op;
845 BitField<3, 3, u64> pred3;
846 BitField<0, 3, u64> pred0;
847 BitField<43, 1, u64> negate_a;
848 BitField<44, 1, u64> abs_a;
849 BitField<47, 2, HalfType> type_a;
850 BitField<31, 1, u64> negate_b;
851 BitField<30, 1, u64> abs_b;
852 BitField<28, 2, HalfType> type_b;
853 BitField<42, 1, u64> neg_pred;
854 BitField<39, 3, u64> pred39;
855 } hsetp2;
856
857 union {
613 BitField<39, 3, u64> pred39; 858 BitField<39, 3, u64> pred39;
614 BitField<42, 1, u64> neg_pred; 859 BitField<42, 1, u64> neg_pred;
615 BitField<43, 1, u64> neg_a; 860 BitField<43, 1, u64> neg_a;
@@ -620,10 +865,24 @@ union Instruction {
620 BitField<53, 1, u64> neg_b; 865 BitField<53, 1, u64> neg_b;
621 BitField<54, 1, u64> abs_a; 866 BitField<54, 1, u64> abs_a;
622 BitField<55, 1, u64> ftz; 867 BitField<55, 1, u64> ftz;
623 BitField<56, 1, u64> neg_imm;
624 } fset; 868 } fset;
625 869
626 union { 870 union {
871 BitField<49, 1, u64> bf;
872 BitField<35, 3, PredCondition> cond;
873 BitField<50, 1, u64> ftz;
874 BitField<45, 2, PredOperation> op;
875 BitField<43, 1, u64> negate_a;
876 BitField<44, 1, u64> abs_a;
877 BitField<47, 2, HalfType> type_a;
878 BitField<31, 1, u64> negate_b;
879 BitField<30, 1, u64> abs_b;
880 BitField<28, 2, HalfType> type_b;
881 BitField<42, 1, u64> neg_pred;
882 BitField<39, 3, u64> pred39;
883 } hset2;
884
885 union {
627 BitField<39, 3, u64> pred39; 886 BitField<39, 3, u64> pred39;
628 BitField<42, 1, u64> neg_pred; 887 BitField<42, 1, u64> neg_pred;
629 BitField<44, 1, u64> bf; 888 BitField<44, 1, u64> bf;
@@ -915,6 +1174,37 @@ union Instruction {
915 } bra; 1174 } bra;
916 1175
917 union { 1176 union {
1177 BitField<39, 1, u64> emit; // EmitVertex
1178 BitField<40, 1, u64> cut; // EndPrimitive
1179 } out;
1180
1181 union {
1182 BitField<31, 1, u64> skew;
1183 BitField<32, 1, u64> o;
1184 BitField<33, 2, IsberdMode> mode;
1185 BitField<47, 2, IsberdShift> shift;
1186 } isberd;
1187
1188 union {
1189 BitField<48, 1, u64> signed_a;
1190 BitField<38, 1, u64> is_byte_chunk_a;
1191 BitField<36, 2, VideoType> type_a;
1192 BitField<36, 2, u64> byte_height_a;
1193
1194 BitField<49, 1, u64> signed_b;
1195 BitField<50, 1, u64> use_register_b;
1196 BitField<30, 1, u64> is_byte_chunk_b;
1197 BitField<28, 2, VideoType> type_b;
1198 BitField<28, 2, u64> byte_height_b;
1199 } video;
1200
1201 union {
1202 BitField<51, 2, VmadShr> shr;
1203 BitField<55, 1, u64> saturate; // Saturates the result (a * b + c)
1204 BitField<47, 1, u64> cc;
1205 } vmad;
1206
1207 union {
918 BitField<20, 16, u64> imm20_16; 1208 BitField<20, 16, u64> imm20_16;
919 BitField<36, 1, u64> product_shift_left; 1209 BitField<36, 1, u64> product_shift_left;
920 BitField<37, 1, u64> merge_37; 1210 BitField<37, 1, u64> merge_37;
@@ -936,10 +1226,16 @@ union Instruction {
936 BitField<36, 5, u64> index; 1226 BitField<36, 5, u64> index;
937 } cbuf36; 1227 } cbuf36;
938 1228
1229 // Unsure about the size of this one.
1230 // It's always used with a gpr0, so any size should be fine.
1231 BitField<20, 8, SystemVariable> sys20;
1232
939 BitField<47, 1, u64> generates_cc; 1233 BitField<47, 1, u64> generates_cc;
940 BitField<61, 1, u64> is_b_imm; 1234 BitField<61, 1, u64> is_b_imm;
941 BitField<60, 1, u64> is_b_gpr; 1235 BitField<60, 1, u64> is_b_gpr;
942 BitField<59, 1, u64> is_c_gpr; 1236 BitField<59, 1, u64> is_c_gpr;
1237 BitField<20, 24, s64> smem_imm;
1238 BitField<0, 5, ControlCode> flow_control_code;
943 1239
944 Attribute attribute; 1240 Attribute attribute;
945 Sampler sampler; 1241 Sampler sampler;
@@ -955,14 +1251,20 @@ public:
955 KIL, 1251 KIL,
956 SSY, 1252 SSY,
957 SYNC, 1253 SYNC,
1254 BRK,
958 DEPBAR, 1255 DEPBAR,
959 BFE_C, 1256 BFE_C,
960 BFE_R, 1257 BFE_R,
961 BFE_IMM, 1258 BFE_IMM,
962 BRA, 1259 BRA,
1260 PBK,
963 LD_A, 1261 LD_A,
1262 LD_L,
1263 LD_S,
964 LD_C, 1264 LD_C,
965 ST_A, 1265 ST_A,
1266 ST_L,
1267 ST_S,
966 LDG, // Load from global memory 1268 LDG, // Load from global memory
967 STG, // Store in global memory 1269 STG, // Store in global memory
968 TEX, 1270 TEX,
@@ -975,6 +1277,10 @@ public:
975 TMML, // Texture Mip Map Level 1277 TMML, // Texture Mip Map Level
976 EXIT, 1278 EXIT,
977 IPA, 1279 IPA,
1280 OUT_R, // Emit vertex/primitive
1281 ISBERD,
1282 VMAD,
1283 VSETP,
978 FFMA_IMM, // Fused Multiply and Add 1284 FFMA_IMM, // Fused Multiply and Add
979 FFMA_CR, 1285 FFMA_CR,
980 FFMA_RC, 1286 FFMA_RC,
@@ -1002,6 +1308,18 @@ public:
1002 LEA_RZ, 1308 LEA_RZ,
1003 LEA_IMM, 1309 LEA_IMM,
1004 LEA_HI, 1310 LEA_HI,
1311 HADD2_C,
1312 HADD2_R,
1313 HADD2_IMM,
1314 HMUL2_C,
1315 HMUL2_R,
1316 HMUL2_IMM,
1317 HFMA2_CR,
1318 HFMA2_RC,
1319 HFMA2_RR,
1320 HFMA2_IMM_R,
1321 HSETP2_R,
1322 HSET2_R,
1005 POPC_C, 1323 POPC_C,
1006 POPC_R, 1324 POPC_R,
1007 POPC_IMM, 1325 POPC_IMM,
@@ -1034,6 +1352,7 @@ public:
1034 MOV_C, 1352 MOV_C,
1035 MOV_R, 1353 MOV_R,
1036 MOV_IMM, 1354 MOV_IMM,
1355 MOV_SYS,
1037 MOV32_IMM, 1356 MOV32_IMM,
1038 SHL_C, 1357 SHL_C,
1039 SHL_R, 1358 SHL_R,
@@ -1074,9 +1393,12 @@ public:
1074 ArithmeticImmediate, 1393 ArithmeticImmediate,
1075 ArithmeticInteger, 1394 ArithmeticInteger,
1076 ArithmeticIntegerImmediate, 1395 ArithmeticIntegerImmediate,
1396 ArithmeticHalf,
1397 ArithmeticHalfImmediate,
1077 Bfe, 1398 Bfe,
1078 Shift, 1399 Shift,
1079 Ffma, 1400 Ffma,
1401 Hfma2,
1080 Flow, 1402 Flow,
1081 Synch, 1403 Synch,
1082 Memory, 1404 Memory,
@@ -1084,6 +1406,8 @@ public:
1084 FloatSetPredicate, 1406 FloatSetPredicate,
1085 IntegerSet, 1407 IntegerSet,
1086 IntegerSetPredicate, 1408 IntegerSetPredicate,
1409 HalfSet,
1410 HalfSetPredicate,
1087 PredicateSetPredicate, 1411 PredicateSetPredicate,
1088 PredicateSetRegister, 1412 PredicateSetRegister,
1089 Conversion, 1413 Conversion,
@@ -1095,7 +1419,7 @@ public:
1095 /// conditionally executed). 1419 /// conditionally executed).
1096 static bool IsPredicatedInstruction(Id opcode) { 1420 static bool IsPredicatedInstruction(Id opcode) {
1097 // TODO(Subv): Add the rest of unpredicated instructions. 1421 // TODO(Subv): Add the rest of unpredicated instructions.
1098 return opcode != Id::SSY; 1422 return opcode != Id::SSY && opcode != Id::PBK;
1099 } 1423 }
1100 1424
1101 class Matcher { 1425 class Matcher {
@@ -1136,7 +1460,7 @@ public:
1136 Type type; 1460 Type type;
1137 }; 1461 };
1138 1462
1139 static boost::optional<const Matcher&> Decode(Instruction instr) { 1463 static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) {
1140 static const auto table{GetDecodeTable()}; 1464 static const auto table{GetDecodeTable()};
1141 1465
1142 const auto matches_instruction = [instr](const auto& matcher) { 1466 const auto matches_instruction = [instr](const auto& matcher) {
@@ -1144,7 +1468,8 @@ public:
1144 }; 1468 };
1145 1469
1146 auto iter = std::find_if(table.begin(), table.end(), matches_instruction); 1470 auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
1147 return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none; 1471 return iter != table.end() ? std::optional<std::reference_wrapper<const Matcher>>(*iter)
1472 : std::nullopt;
1148 } 1473 }
1149 1474
1150private: 1475private:
@@ -1191,12 +1516,18 @@ private:
1191#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) 1516#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
1192 INST("111000110011----", Id::KIL, Type::Flow, "KIL"), 1517 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
1193 INST("111000101001----", Id::SSY, Type::Flow, "SSY"), 1518 INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
1519 INST("111000101010----", Id::PBK, Type::Flow, "PBK"),
1194 INST("111000100100----", Id::BRA, Type::Flow, "BRA"), 1520 INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
1521 INST("1111000011111---", Id::SYNC, Type::Flow, "SYNC"),
1522 INST("111000110100---", Id::BRK, Type::Flow, "BRK"),
1195 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), 1523 INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
1196 INST("1111000011111---", Id::SYNC, Type::Synch, "SYNC"),
1197 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), 1524 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
1525 INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
1526 INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
1198 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), 1527 INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
1199 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), 1528 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
1529 INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"),
1530 INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
1200 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), 1531 INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
1201 INST("1110111011011---", Id::STG, Type::Memory, "STG"), 1532 INST("1110111011011---", Id::STG, Type::Memory, "STG"),
1202 INST("110000----111---", Id::TEX, Type::Memory, "TEX"), 1533 INST("110000----111---", Id::TEX, Type::Memory, "TEX"),
@@ -1209,6 +1540,10 @@ private:
1209 INST("1101111101011---", Id::TMML, Type::Memory, "TMML"), 1540 INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
1210 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), 1541 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
1211 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 1542 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
1543 INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
1544 INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
1545 INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"),
1546 INST("0101000011110---", Id::VSETP, Type::Trivial, "VSETP"),
1212 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), 1547 INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
1213 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), 1548 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
1214 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), 1549 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1242,6 +1577,18 @@ private:
1242 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), 1577 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
1243 INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"), 1578 INST("010010111101----", Id::LEA_RZ, Type::ArithmeticInteger, "LEA_RZ"),
1244 INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"), 1579 INST("00011000--------", Id::LEA_HI, Type::ArithmeticInteger, "LEA_HI"),
1580 INST("0111101-1-------", Id::HADD2_C, Type::ArithmeticHalf, "HADD2_C"),
1581 INST("0101110100010---", Id::HADD2_R, Type::ArithmeticHalf, "HADD2_R"),
1582 INST("0111101-0-------", Id::HADD2_IMM, Type::ArithmeticHalfImmediate, "HADD2_IMM"),
1583 INST("0111100-1-------", Id::HMUL2_C, Type::ArithmeticHalf, "HMUL2_C"),
1584 INST("0101110100001---", Id::HMUL2_R, Type::ArithmeticHalf, "HMUL2_R"),
1585 INST("0111100-0-------", Id::HMUL2_IMM, Type::ArithmeticHalfImmediate, "HMUL2_IMM"),
1586 INST("01110---1-------", Id::HFMA2_CR, Type::Hfma2, "HFMA2_CR"),
1587 INST("01100---1-------", Id::HFMA2_RC, Type::Hfma2, "HFMA2_RC"),
1588 INST("0101110100000---", Id::HFMA2_RR, Type::Hfma2, "HFMA2_RR"),
1589 INST("01110---0-------", Id::HFMA2_IMM_R, Type::Hfma2, "HFMA2_R_IMM"),
1590 INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP_R"),
1591 INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
1245 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), 1592 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
1246 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), 1593 INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
1247 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), 1594 INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
@@ -1255,6 +1602,7 @@ private:
1255 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), 1602 INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
1256 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), 1603 INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
1257 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), 1604 INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
1605 INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"),
1258 INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"), 1606 INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
1259 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), 1607 INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
1260 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), 1608 INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h
index a885ee3cf..a0e015c4b 100644
--- a/src/video_core/engines/shader_header.h
+++ b/src/video_core/engines/shader_header.h
@@ -96,6 +96,11 @@ struct Header {
96 } 96 }
97 } ps; 97 } ps;
98 }; 98 };
99
100 u64 GetLocalMemorySize() {
101 return (common1.shader_local_memory_low_size |
102 (common2.shader_local_memory_high_size << 24));
103 }
99}; 104};
100 105
101static_assert(sizeof(Header) == 0x50, "Incorrect structure size"); 106static_assert(sizeof(Header) == 0x50, "Incorrect structure size");
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index baa8b63b7..83c7e5b0b 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -25,10 +25,10 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
25GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { 25GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
26 memory_manager = std::make_unique<Tegra::MemoryManager>(); 26 memory_manager = std::make_unique<Tegra::MemoryManager>();
27 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager); 27 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
28 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); 28 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
29 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 29 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
30 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); 30 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
31 kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager); 31 kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
32} 32}
33 33
34GPU::~GPU() = default; 34GPU::~GPU() = default;
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 377bd66ab..335a8d407 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -11,7 +11,7 @@ namespace Tegra {
11 11
12MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {} 12MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
13 13
14void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> parameters) { 14void MacroInterpreter::Execute(u32 offset, std::vector<u32> parameters) {
15 Reset(); 15 Reset();
16 registers[1] = parameters[0]; 16 registers[1] = parameters[0];
17 this->parameters = std::move(parameters); 17 this->parameters = std::move(parameters);
@@ -19,7 +19,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa
19 // Execute the code until we hit an exit condition. 19 // Execute the code until we hit an exit condition.
20 bool keep_executing = true; 20 bool keep_executing = true;
21 while (keep_executing) { 21 while (keep_executing) {
22 keep_executing = Step(code, false); 22 keep_executing = Step(offset, false);
23 } 23 }
24 24
25 // Assert the the macro used all the input parameters 25 // Assert the the macro used all the input parameters
@@ -29,7 +29,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa
29void MacroInterpreter::Reset() { 29void MacroInterpreter::Reset() {
30 registers = {}; 30 registers = {};
31 pc = 0; 31 pc = 0;
32 delayed_pc = boost::none; 32 delayed_pc = {};
33 method_address.raw = 0; 33 method_address.raw = 0;
34 parameters.clear(); 34 parameters.clear();
35 // The next parameter index starts at 1, because $r1 already has the value of the first 35 // The next parameter index starts at 1, because $r1 already has the value of the first
@@ -37,17 +37,17 @@ void MacroInterpreter::Reset() {
37 next_parameter_index = 1; 37 next_parameter_index = 1;
38} 38}
39 39
40bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) { 40bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
41 u32 base_address = pc; 41 u32 base_address = pc;
42 42
43 Opcode opcode = GetOpcode(code); 43 Opcode opcode = GetOpcode(offset);
44 pc += 4; 44 pc += 4;
45 45
46 // Update the program counter if we were delayed 46 // Update the program counter if we were delayed
47 if (delayed_pc != boost::none) { 47 if (delayed_pc) {
48 ASSERT(is_delay_slot); 48 ASSERT(is_delay_slot);
49 pc = *delayed_pc; 49 pc = *delayed_pc;
50 delayed_pc = boost::none; 50 delayed_pc = {};
51 } 51 }
52 52
53 switch (opcode.operation) { 53 switch (opcode.operation) {
@@ -108,7 +108,7 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
108 108
109 delayed_pc = base_address + opcode.GetBranchTarget(); 109 delayed_pc = base_address + opcode.GetBranchTarget();
110 // Execute one more instruction due to the delay slot. 110 // Execute one more instruction due to the delay slot.
111 return Step(code, true); 111 return Step(offset, true);
112 } 112 }
113 break; 113 break;
114 } 114 }
@@ -121,17 +121,18 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
121 // Exit has a delay slot, execute the next instruction 121 // Exit has a delay slot, execute the next instruction
122 // Note: Executing an exit during a branch delay slot will cause the instruction at the 122 // Note: Executing an exit during a branch delay slot will cause the instruction at the
123 // branch target to be executed before exiting. 123 // branch target to be executed before exiting.
124 Step(code, true); 124 Step(offset, true);
125 return false; 125 return false;
126 } 126 }
127 127
128 return true; 128 return true;
129} 129}
130 130
131MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector<u32>& code) const { 131MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
132 const auto& macro_memory{maxwell3d.GetMacroMemory()};
132 ASSERT((pc % sizeof(u32)) == 0); 133 ASSERT((pc % sizeof(u32)) == 0);
133 ASSERT(pc < code.size() * sizeof(u32)); 134 ASSERT((pc + offset) < macro_memory.size() * sizeof(u32));
134 return {code[pc / sizeof(u32)]}; 135 return {macro_memory[offset + pc / sizeof(u32)]};
135} 136}
136 137
137u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const { 138u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h
index cee0baaf3..62d1ce289 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro_interpreter.h
@@ -5,8 +5,9 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8#include <vector> 9#include <vector>
9#include <boost/optional.hpp> 10
10#include "common/bit_field.h" 11#include "common/bit_field.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
@@ -21,10 +22,10 @@ public:
21 22
22 /** 23 /**
23 * Executes the macro code with the specified input parameters. 24 * Executes the macro code with the specified input parameters.
24 * @param code The macro byte code to execute 25 * @param offset Offset to start execution at.
25 * @param parameters The parameters of the macro 26 * @param parameters The parameters of the macro.
26 */ 27 */
27 void Execute(const std::vector<u32>& code, std::vector<u32> parameters); 28 void Execute(u32 offset, std::vector<u32> parameters);
28 29
29private: 30private:
30 enum class Operation : u32 { 31 enum class Operation : u32 {
@@ -109,11 +110,11 @@ private:
109 /** 110 /**
110 * Executes a single macro instruction located at the current program counter. Returns whether 111 * Executes a single macro instruction located at the current program counter. Returns whether
111 * the interpreter should keep running. 112 * the interpreter should keep running.
112 * @param code The macro code to execute. 113 * @param offset Offset to start execution at.
113 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a 114 * @param is_delay_slot Whether the current step is being executed due to a delay slot in a
114 * previous instruction. 115 * previous instruction.
115 */ 116 */
116 bool Step(const std::vector<u32>& code, bool is_delay_slot); 117 bool Step(u32 offset, bool is_delay_slot);
117 118
118 /// Calculates the result of an ALU operation. src_a OP src_b; 119 /// Calculates the result of an ALU operation. src_a OP src_b;
119 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const; 120 u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
@@ -126,7 +127,7 @@ private:
126 bool EvaluateBranchCondition(BranchCondition cond, u32 value) const; 127 bool EvaluateBranchCondition(BranchCondition cond, u32 value) const;
127 128
128 /// Reads an opcode at the current program counter location. 129 /// Reads an opcode at the current program counter location.
129 Opcode GetOpcode(const std::vector<u32>& code) const; 130 Opcode GetOpcode(u32 offset) const;
130 131
131 /// Returns the specified register's value. Register 0 is hardcoded to always return 0. 132 /// Returns the specified register's value. Register 0 is hardcoded to always return 0.
132 u32 GetRegister(u32 register_id) const; 133 u32 GetRegister(u32 register_id) const;
@@ -149,7 +150,7 @@ private:
149 Engines::Maxwell3D& maxwell3d; 150 Engines::Maxwell3D& maxwell3d;
150 151
151 u32 pc; ///< Current program counter 152 u32 pc; ///< Current program counter
152 boost::optional<u32> 153 std::optional<u32>
153 delayed_pc; ///< Program counter to execute at after the delay slot is executed. 154 delayed_pc; ///< Program counter to execute at after the delay slot is executed.
154 155
155 static constexpr std::size_t NumMacroRegisters = 8; 156 static constexpr std::size_t NumMacroRegisters = 8;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index ca923d17d..90a8e825d 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -9,7 +9,7 @@
9namespace Tegra { 9namespace Tegra {
10 10
11GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { 11GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) {
12 boost::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, align); 12 std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, align);
13 ASSERT(gpu_addr); 13 ASSERT(gpu_addr);
14 14
15 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 15 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
@@ -34,7 +34,7 @@ GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) {
34} 34}
35 35
36GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { 36GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
37 boost::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, PAGE_SIZE); 37 std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, PAGE_SIZE);
38 ASSERT(gpu_addr); 38 ASSERT(gpu_addr);
39 39
40 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { 40 for (u64 offset = 0; offset < size; offset += PAGE_SIZE) {
@@ -87,7 +87,17 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
87 return gpu_addr; 87 return gpu_addr;
88} 88}
89 89
90boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { 90GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const {
91 for (const auto& region : mapped_regions) {
92 const GPUVAddr region_end{region.gpu_addr + region.size};
93 if (region_start >= region.gpu_addr && region_start < region_end) {
94 return region_end;
95 }
96 }
97 return {};
98}
99
100std::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
91 GPUVAddr gpu_addr = 0; 101 GPUVAddr gpu_addr = 0;
92 u64 free_space = 0; 102 u64 free_space = 0;
93 align = (align + PAGE_MASK) & ~PAGE_MASK; 103 align = (align + PAGE_MASK) & ~PAGE_MASK;
@@ -108,7 +118,7 @@ boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {
108 return {}; 118 return {};
109} 119}
110 120
111boost::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { 121std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) {
112 VAddr base_addr = PageSlot(gpu_addr); 122 VAddr base_addr = PageSlot(gpu_addr);
113 123
114 if (base_addr == static_cast<u64>(PageStatus::Allocated) || 124 if (base_addr == static_cast<u64>(PageStatus::Allocated) ||
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 86765e72a..b1255fd56 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -6,10 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <vector> 10#include <vector>
10 11
11#include <boost/optional.hpp>
12
13#include "common/common_types.h" 12#include "common/common_types.h"
14 13
15namespace Tegra { 14namespace Tegra {
@@ -26,7 +25,8 @@ public:
26 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size); 25 GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
27 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); 26 GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);
28 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); 27 GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size);
29 boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); 28 GPUVAddr GetRegionEnd(GPUVAddr region_start) const;
29 std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);
30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; 30 std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const;
31 31
32 static constexpr u64 PAGE_BITS = 16; 32 static constexpr u64 PAGE_BITS = 16;
@@ -34,7 +34,7 @@ public:
34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 34 static constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
35 35
36private: 36private:
37 boost::optional<GPUVAddr> FindFreeBlock(u64 size, u64 align = 1); 37 std::optional<GPUVAddr> FindFreeBlock(u64 size, u64 align = 1);
38 bool IsPageMapped(GPUVAddr gpu_addr); 38 bool IsPageMapped(GPUVAddr gpu_addr);
39 VAddr& PageSlot(GPUVAddr gpu_addr); 39 VAddr& PageSlot(GPUVAddr gpu_addr);
40 40
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 083b283b0..0a3b3951e 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -11,32 +11,77 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/settings.h"
14#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
16 17
18class RasterizerCacheObject {
19public:
20 /// Gets the address of the shader in guest memory, required for cache management
21 virtual VAddr GetAddr() const = 0;
22
23 /// Gets the size of the shader in guest memory, required for cache management
24 virtual std::size_t GetSizeInBytes() const = 0;
25
26 /// Wriets any cached resources back to memory
27 virtual void Flush() = 0;
28
29 /// Sets whether the cached object should be considered registered
30 void SetIsRegistered(bool registered) {
31 is_registered = registered;
32 }
33
34 /// Returns true if the cached object is registered
35 bool IsRegistered() const {
36 return is_registered;
37 }
38
39 /// Returns true if the cached object is dirty
40 bool IsDirty() const {
41 return is_dirty;
42 }
43
44 /// Returns ticks from when this cached object was last modified
45 u64 GetLastModifiedTicks() const {
46 return last_modified_ticks;
47 }
48
49 /// Marks an object as recently modified, used to specify whether it is clean or dirty
50 template <class T>
51 void MarkAsModified(bool dirty, T& cache) {
52 is_dirty = dirty;
53 last_modified_ticks = cache.GetModifiedTicks();
54 }
55
56private:
57 bool is_registered{}; ///< Whether the object is currently registered with the cache
58 bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
59 u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
60};
61
17template <class T> 62template <class T>
18class RasterizerCache : NonCopyable { 63class RasterizerCache : NonCopyable {
64 friend class RasterizerCacheObject;
65
19public: 66public:
67 /// Write any cached resources overlapping the specified region back to memory
68 void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
69 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
70 for (auto& object : objects) {
71 FlushObject(object);
72 }
73 }
74
20 /// Mark the specified region as being invalidated 75 /// Mark the specified region as being invalidated
21 void InvalidateRegion(VAddr addr, u64 size) { 76 void InvalidateRegion(VAddr addr, u64 size) {
22 if (size == 0) 77 const auto& objects{GetSortedObjectsFromRegion(addr, size)};
23 return; 78 for (auto& object : objects) {
24 79 if (!object->IsRegistered()) {
25 const ObjectInterval interval{addr, addr + size}; 80 // Skip duplicates
26 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { 81 continue;
27 for (auto& cached_object : pair.second) {
28 if (!cached_object)
29 continue;
30
31 remove_objects.emplace(cached_object);
32 } 82 }
83 Unregister(object);
33 } 84 }
34
35 for (auto& remove_object : remove_objects) {
36 Unregister(remove_object);
37 }
38
39 remove_objects.clear();
40 } 85 }
41 86
42 /// Invalidates everything in the cache 87 /// Invalidates everything in the cache
@@ -62,6 +107,7 @@ protected:
62 107
63 /// Register an object into the cache 108 /// Register an object into the cache
64 void Register(const T& object) { 109 void Register(const T& object) {
110 object->SetIsRegistered(true);
65 object_cache.add({GetInterval(object), ObjectSet{object}}); 111 object_cache.add({GetInterval(object), ObjectSet{object}});
66 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 112 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
67 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); 113 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
@@ -69,12 +115,57 @@ protected:
69 115
70 /// Unregisters an object from the cache 116 /// Unregisters an object from the cache
71 void Unregister(const T& object) { 117 void Unregister(const T& object) {
118 object->SetIsRegistered(false);
72 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer(); 119 auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
73 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); 120 rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
121
122 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
123 if (Settings::values.use_accurate_gpu_emulation) {
124 FlushObject(object);
125 }
126
74 object_cache.subtract({GetInterval(object), ObjectSet{object}}); 127 object_cache.subtract({GetInterval(object), ObjectSet{object}});
75 } 128 }
76 129
130 /// Returns a ticks counter used for tracking when cached objects were last modified
131 u64 GetModifiedTicks() {
132 return ++modified_ticks;
133 }
134
77private: 135private:
136 /// Returns a list of cached objects from the specified memory region, ordered by access time
137 std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
138 if (size == 0) {
139 return {};
140 }
141
142 std::vector<T> objects;
143 const ObjectInterval interval{addr, addr + size};
144 for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
145 for (auto& cached_object : pair.second) {
146 if (!cached_object) {
147 continue;
148 }
149 objects.push_back(cached_object);
150 }
151 }
152
153 std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
154 return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
155 });
156
157 return objects;
158 }
159
160 /// Flushes the specified object, updating appropriate cache state as needed
161 void FlushObject(const T& object) {
162 if (!object->IsDirty()) {
163 return;
164 }
165 object->Flush();
166 object->MarkAsModified(false, *this);
167 }
168
78 using ObjectSet = std::set<T>; 169 using ObjectSet = std::set<T>;
79 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>; 170 using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
80 using ObjectInterval = typename ObjectCache::interval_type; 171 using ObjectInterval = typename ObjectCache::interval_type;
@@ -84,6 +175,6 @@ private:
84 object->GetAddr() + object->GetSizeInBytes()); 175 object->GetAddr() + object->GetSizeInBytes());
85 } 176 }
86 177
87 ObjectCache object_cache; 178 ObjectCache object_cache; ///< Cache of objects
88 ObjectSet remove_objects; 179 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
89}; 180};
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index cd819d69f..06fc59dbe 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "video_core/engines/fermi_2d.h"
8#include "video_core/gpu.h" 9#include "video_core/gpu.h"
9#include "video_core/memory_manager.h" 10#include "video_core/memory_manager.h"
10 11
@@ -33,13 +34,9 @@ public:
33 /// and invalidated 34 /// and invalidated
34 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0; 35 virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
35 36
36 /// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0 37 /// Attempt to use a faster method to perform a surface copy
37 virtual bool AccelerateDisplayTransfer(const void* config) { 38 virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
38 return false; 39 const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
39 }
40
41 /// Attempt to use a faster method to perform a display transfer with is_texture_copy = 1
42 virtual bool AccelerateTextureCopy(const void* config) {
43 return false; 40 return false;
44 } 41 }
45 42
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 2cd0738ff..669e26e15 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -6,7 +6,8 @@
6 6
7#include <atomic> 7#include <atomic>
8#include <memory> 8#include <memory>
9#include <boost/optional.hpp> 9#include <optional>
10
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "video_core/gpu.h" 12#include "video_core/gpu.h"
12#include "video_core/rasterizer_interface.h" 13#include "video_core/rasterizer_interface.h"
@@ -28,7 +29,8 @@ public:
28 virtual ~RendererBase(); 29 virtual ~RendererBase();
29 30
30 /// Swap buffers (render frame) 31 /// Swap buffers (render frame)
31 virtual void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) = 0; 32 virtual void SwapBuffers(
33 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0;
32 34
33 /// Initialize the renderer 35 /// Initialize the renderer
34 virtual bool Init() = 0; 36 virtual bool Init() = 0;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index c142095c5..41a54b3e7 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -17,7 +17,7 @@ OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER
17GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, 17GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size,
18 std::size_t alignment, bool cache) { 18 std::size_t alignment, bool cache) {
19 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 19 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
20 const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; 20 const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
21 21
22 // Cache management is a big overhead, so only cache entries with a given size. 22 // Cache management is a big overhead, so only cache entries with a given size.
23 // TODO: Figure out which size is the best for given games. 23 // TODO: Figure out which size is the best for given games.
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 965976334..be29dc8be 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -15,15 +15,18 @@
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18struct CachedBufferEntry final { 18struct CachedBufferEntry final : public RasterizerCacheObject {
19 VAddr GetAddr() const { 19 VAddr GetAddr() const override {
20 return addr; 20 return addr;
21 } 21 }
22 22
23 std::size_t GetSizeInBytes() const { 23 std::size_t GetSizeInBytes() const override {
24 return size; 24 return size;
25 } 25 }
26 26
27 // We do not have to flush this cache as things in it are never modified by us.
28 void Flush() override {}
29
27 VAddr addr; 30 VAddr addr;
28 std::size_t size; 31 std::size_t size;
29 GLintptr offset; 32 GLintptr offset;
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
index ee1d9601b..741f14bc3 100644
--- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
+++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp
@@ -45,7 +45,7 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size
45 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); 45 auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
46 46
47 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 47 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
48 const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; 48 const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
49 const u8* source{Memory::GetPointer(*cpu_addr)}; 49 const u8* source{Memory::GetPointer(*cpu_addr)};
50 50
51 for (u32 primitive = 0; primitive < count / 4; ++primitive) { 51 for (u32 primitive = 0; primitive < count / 4; ++primitive) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a91bc6dee..a0527fe57 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -30,8 +30,8 @@
30namespace OpenGL { 30namespace OpenGL {
31 31
32using Maxwell = Tegra::Engines::Maxwell3D::Regs; 32using Maxwell = Tegra::Engines::Maxwell3D::Regs;
33using PixelFormat = SurfaceParams::PixelFormat; 33using PixelFormat = VideoCore::Surface::PixelFormat;
34using SurfaceType = SurfaceParams::SurfaceType; 34using SurfaceType = VideoCore::Surface::SurfaceType;
35 35
36MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); 36MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
37MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192)); 37MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
@@ -104,7 +104,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
104 } 104 }
105 105
106 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); 106 ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
107 107 OpenGLState::ApplyDefaultState();
108 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 108 // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
109 state.clip_distance[0] = true; 109 state.clip_distance[0] = true;
110 110
@@ -115,8 +115,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
115 state.draw.shader_program = 0; 115 state.draw.shader_program = 0;
116 state.Apply(); 116 state.Apply();
117 117
118 glEnable(GL_BLEND);
119
120 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); 118 glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
121 119
122 LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); 120 LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!");
@@ -255,7 +253,7 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
255 return params; 253 return params;
256} 254}
257 255
258void RasterizerOpenGL::SetupShaders() { 256void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
259 MICROPROFILE_SCOPE(OpenGL_Shader); 257 MICROPROFILE_SCOPE(OpenGL_Shader);
260 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); 258 const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
261 259
@@ -270,6 +268,11 @@ void RasterizerOpenGL::SetupShaders() {
270 268
271 // Skip stages that are not enabled 269 // Skip stages that are not enabled
272 if (!gpu.regs.IsShaderConfigEnabled(index)) { 270 if (!gpu.regs.IsShaderConfigEnabled(index)) {
271 switch (program) {
272 case Maxwell::ShaderProgram::Geometry:
273 shader_program_manager->UseTrivialGeometryShader();
274 break;
275 }
273 continue; 276 continue;
274 } 277 }
275 278
@@ -281,18 +284,26 @@ void RasterizerOpenGL::SetupShaders() {
281 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 284 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
282 285
283 // Bind the buffer 286 // Bind the buffer
284 glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); 287 glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
288 offset, static_cast<GLsizeiptr>(sizeof(ubo)));
285 289
286 Shader shader{shader_cache.GetStageProgram(program)}; 290 Shader shader{shader_cache.GetStageProgram(program)};
287 291
288 switch (program) { 292 switch (program) {
289 case Maxwell::ShaderProgram::VertexA: 293 case Maxwell::ShaderProgram::VertexA:
290 case Maxwell::ShaderProgram::VertexB: { 294 case Maxwell::ShaderProgram::VertexB: {
291 shader_program_manager->UseProgrammableVertexShader(shader->GetProgramHandle()); 295 shader_program_manager->UseProgrammableVertexShader(
296 shader->GetProgramHandle(primitive_mode));
297 break;
298 }
299 case Maxwell::ShaderProgram::Geometry: {
300 shader_program_manager->UseProgrammableGeometryShader(
301 shader->GetProgramHandle(primitive_mode));
292 break; 302 break;
293 } 303 }
294 case Maxwell::ShaderProgram::Fragment: { 304 case Maxwell::ShaderProgram::Fragment: {
295 shader_program_manager->UseProgrammableFragmentShader(shader->GetProgramHandle()); 305 shader_program_manager->UseProgrammableFragmentShader(
306 shader->GetProgramHandle(primitive_mode));
296 break; 307 break;
297 } 308 }
298 default: 309 default:
@@ -302,12 +313,13 @@ void RasterizerOpenGL::SetupShaders() {
302 } 313 }
303 314
304 // Configure the const buffers for this shader stage. 315 // Configure the const buffers for this shader stage.
305 current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), 316 current_constbuffer_bindpoint =
306 shader, current_constbuffer_bindpoint); 317 SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
318 current_constbuffer_bindpoint);
307 319
308 // Configure the textures for this shader stage. 320 // Configure the textures for this shader stage.
309 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader, 321 current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
310 current_texture_bindpoint); 322 primitive_mode, current_texture_bindpoint);
311 323
312 // When VertexA is enabled, we have dual vertex shaders 324 // When VertexA is enabled, we have dual vertex shaders
313 if (program == Maxwell::ShaderProgram::VertexA) { 325 if (program == Maxwell::ShaderProgram::VertexA) {
@@ -317,8 +329,6 @@ void RasterizerOpenGL::SetupShaders() {
317 } 329 }
318 330
319 state.Apply(); 331 state.Apply();
320
321 shader_program_manager->UseTrivialGeometryShader();
322} 332}
323 333
324std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 334std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -389,7 +399,7 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
389 399
390void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, 400void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb,
391 bool preserve_contents, 401 bool preserve_contents,
392 boost::optional<std::size_t> single_color_target) { 402 std::optional<std::size_t> single_color_target) {
393 MICROPROFILE_SCOPE(OpenGL_Framebuffer); 403 MICROPROFILE_SCOPE(OpenGL_Framebuffer);
394 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 404 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
395 405
@@ -406,12 +416,23 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
406 // Bind the framebuffer surfaces 416 // Bind the framebuffer surfaces
407 state.draw.draw_framebuffer = framebuffer.handle; 417 state.draw.draw_framebuffer = framebuffer.handle;
408 state.Apply(); 418 state.Apply();
419 state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
409 420
410 if (using_color_fb) { 421 if (using_color_fb) {
411 if (single_color_target) { 422 if (single_color_target) {
412 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer 423 // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
413 Surface color_surface = 424 Surface color_surface =
414 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents); 425 res_cache.GetColorBufferSurface(*single_color_target, preserve_contents);
426
427 if (color_surface) {
428 // Assume that a surface will be written to if it is used as a framebuffer, even if
429 // the shader doesn't actually write to it.
430 color_surface->MarkAsModified(true, res_cache);
431 // Workaround for and issue in nvidia drivers
432 // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
433 state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
434 }
435
415 glFramebufferTexture2D( 436 glFramebufferTexture2D(
416 GL_DRAW_FRAMEBUFFER, 437 GL_DRAW_FRAMEBUFFER,
417 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, 438 GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D,
@@ -422,6 +443,18 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
422 std::array<GLenum, Maxwell::NumRenderTargets> buffers; 443 std::array<GLenum, Maxwell::NumRenderTargets> buffers;
423 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { 444 for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
424 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); 445 Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
446
447 if (color_surface) {
448 // Assume that a surface will be written to if it is used as a framebuffer, even
449 // if the shader doesn't actually write to it.
450 color_surface->MarkAsModified(true, res_cache);
451 // Enable sRGB only for supported formats
452 // Workaround for and issue in nvidia drivers
453 // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
454 state.framebuffer_srgb.enabled |=
455 color_surface->GetSurfaceParams().srgb_conversion;
456 }
457
425 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); 458 buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
426 glFramebufferTexture2D( 459 glFramebufferTexture2D(
427 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), 460 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index),
@@ -441,6 +474,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep
441 } 474 }
442 475
443 if (depth_surface) { 476 if (depth_surface) {
477 // Assume that a surface will be written to if it is used as a framebuffer, even if
478 // the shader doesn't actually write to it.
479 depth_surface->MarkAsModified(true, res_cache);
480
444 if (regs.stencil_enable) { 481 if (regs.stencil_enable) {
445 // Attach both depth and stencil 482 // Attach both depth and stencil
446 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 483 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
@@ -507,7 +544,9 @@ void RasterizerOpenGL::Clear() {
507 544
508 ConfigureFramebuffers(use_color, use_depth || use_stencil, false, 545 ConfigureFramebuffers(use_color, use_depth || use_stencil, false,
509 regs.clear_buffers.RT.Value()); 546 regs.clear_buffers.RT.Value());
510 547 // Copy the sRGB setting to the clear state to avoid problem with
548 // specific driver implementations
549 clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled;
511 clear_state.Apply(); 550 clear_state.Apply();
512 551
513 if (use_color) { 552 if (use_color) {
@@ -540,9 +579,13 @@ void RasterizerOpenGL::DrawArrays() {
540 SyncBlendState(); 579 SyncBlendState();
541 SyncLogicOpState(); 580 SyncLogicOpState();
542 SyncCullMode(); 581 SyncCullMode();
543 SyncAlphaTest(); 582 SyncPrimitiveRestart();
583 SyncDepthRange();
584 SyncScissorTest();
585 // Alpha Testing is synced on shaders.
544 SyncTransformFeedback(); 586 SyncTransformFeedback();
545 SyncPointState(); 587 SyncPointState();
588 CheckAlphaTests();
546 589
547 // TODO(bunnei): Sync framebuffer_scale uniform here 590 // TODO(bunnei): Sync framebuffer_scale uniform here
548 // TODO(bunnei): Sync scissorbox uniform(s) here 591 // TODO(bunnei): Sync scissorbox uniform(s) here
@@ -580,7 +623,7 @@ void RasterizerOpenGL::DrawArrays() {
580 623
581 SetupVertexArrays(); 624 SetupVertexArrays();
582 DrawParameters params = SetupDraw(); 625 DrawParameters params = SetupDraw();
583 SetupShaders(); 626 SetupShaders(params.primitive_mode);
584 627
585 buffer_cache.Unmap(); 628 buffer_cache.Unmap();
586 629
@@ -604,7 +647,14 @@ void RasterizerOpenGL::DrawArrays() {
604 647
605void RasterizerOpenGL::FlushAll() {} 648void RasterizerOpenGL::FlushAll() {}
606 649
607void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {} 650void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
651 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
652
653 if (Settings::values.use_accurate_gpu_emulation) {
654 // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
655 res_cache.FlushRegion(addr, size);
656 }
657}
608 658
609void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) { 659void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
610 MICROPROFILE_SCOPE(OpenGL_CacheManagement); 660 MICROPROFILE_SCOPE(OpenGL_CacheManagement);
@@ -614,17 +664,20 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
614} 664}
615 665
616void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { 666void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
667 FlushRegion(addr, size);
617 InvalidateRegion(addr, size); 668 InvalidateRegion(addr, size);
618} 669}
619 670
620bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) { 671bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
672 const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
621 MICROPROFILE_SCOPE(OpenGL_Blits); 673 MICROPROFILE_SCOPE(OpenGL_Blits);
622 UNREACHABLE();
623 return true;
624}
625 674
626bool RasterizerOpenGL::AccelerateTextureCopy(const void* config) { 675 if (Settings::values.use_accurate_gpu_emulation) {
627 UNREACHABLE(); 676 // Skip the accelerated copy and perform a slow but more accurate copy
677 return false;
678 }
679
680 res_cache.FermiCopySurface(src, dst);
628 return true; 681 return true;
629} 682}
630 683
@@ -648,7 +701,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
648 701
649 // Verify that the cached surface is the same size and format as the requested framebuffer 702 // Verify that the cached surface is the same size and format as the requested framebuffer
650 const auto& params{surface->GetSurfaceParams()}; 703 const auto& params{surface->GetSurfaceParams()};
651 const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)}; 704 const auto& pixel_format{
705 VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)};
652 ASSERT_MSG(params.width == config.width, "Framebuffer width is different"); 706 ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
653 ASSERT_MSG(params.height == config.height, "Framebuffer height is different"); 707 ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
654 ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different"); 708 ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different");
@@ -662,10 +716,13 @@ void RasterizerOpenGL::SamplerInfo::Create() {
662 sampler.Create(); 716 sampler.Create();
663 mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; 717 mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
664 wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap; 718 wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap;
719 uses_depth_compare = false;
720 depth_compare_func = Tegra::Texture::DepthCompareFunc::Never;
665 721
666 // default is GL_LINEAR_MIPMAP_LINEAR 722 // default is GL_LINEAR_MIPMAP_LINEAR
667 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 723 glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
668 // Other attributes have correct defaults 724 // Other attributes have correct defaults
725 glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
669} 726}
670 727
671void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) { 728void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
@@ -673,11 +730,15 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
673 730
674 if (mag_filter != config.mag_filter) { 731 if (mag_filter != config.mag_filter) {
675 mag_filter = config.mag_filter; 732 mag_filter = config.mag_filter;
676 glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, MaxwellToGL::TextureFilterMode(mag_filter)); 733 glSamplerParameteri(
734 s, GL_TEXTURE_MAG_FILTER,
735 MaxwellToGL::TextureFilterMode(mag_filter, Tegra::Texture::TextureMipmapFilter::None));
677 } 736 }
678 if (min_filter != config.min_filter) { 737 if (min_filter != config.min_filter || mip_filter != config.mip_filter) {
679 min_filter = config.min_filter; 738 min_filter = config.min_filter;
680 glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, MaxwellToGL::TextureFilterMode(min_filter)); 739 mip_filter = config.mip_filter;
740 glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
741 MaxwellToGL::TextureFilterMode(min_filter, mip_filter));
681 } 742 }
682 743
683 if (wrap_u != config.wrap_u) { 744 if (wrap_u != config.wrap_u) {
@@ -693,6 +754,21 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
693 glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p)); 754 glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
694 } 755 }
695 756
757 if (uses_depth_compare != (config.depth_compare_enabled == 1)) {
758 uses_depth_compare = (config.depth_compare_enabled == 1);
759 if (uses_depth_compare) {
760 glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
761 } else {
762 glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, GL_NONE);
763 }
764 }
765
766 if (depth_compare_func != config.depth_compare_func) {
767 depth_compare_func = config.depth_compare_func;
768 glSamplerParameteri(s, GL_TEXTURE_COMPARE_FUNC,
769 MaxwellToGL::DepthCompareFunc(depth_compare_func));
770 }
771
696 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border || 772 if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
697 wrap_p == Tegra::Texture::WrapMode::Border) { 773 wrap_p == Tegra::Texture::WrapMode::Border) {
698 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, 774 const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
@@ -705,7 +781,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
705} 781}
706 782
707u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader, 783u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
708 u32 current_bindpoint) { 784 GLenum primitive_mode, u32 current_bindpoint) {
709 MICROPROFILE_SCOPE(OpenGL_UBO); 785 MICROPROFILE_SCOPE(OpenGL_UBO);
710 const auto& gpu = Core::System::GetInstance().GPU(); 786 const auto& gpu = Core::System::GetInstance().GPU();
711 const auto& maxwell3d = gpu.Maxwell3D(); 787 const auto& maxwell3d = gpu.Maxwell3D();
@@ -757,7 +833,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
757 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); 833 buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
758 834
759 // Now configure the bindpoint of the buffer inside the shader 835 // Now configure the bindpoint of the buffer inside the shader
760 glUniformBlockBinding(shader->GetProgramHandle(), 836 glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
761 shader->GetProgramResourceIndex(used_buffer), 837 shader->GetProgramResourceIndex(used_buffer),
762 current_bindpoint + bindpoint); 838 current_bindpoint + bindpoint);
763 839
@@ -773,7 +849,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
773 return current_bindpoint + static_cast<u32>(entries.size()); 849 return current_bindpoint + static_cast<u32>(entries.size());
774} 850}
775 851
776u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) { 852u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
853 GLenum primitive_mode, u32 current_unit) {
777 MICROPROFILE_SCOPE(OpenGL_Texture); 854 MICROPROFILE_SCOPE(OpenGL_Texture);
778 const auto& gpu = Core::System::GetInstance().GPU(); 855 const auto& gpu = Core::System::GetInstance().GPU();
779 const auto& maxwell3d = gpu.Maxwell3D(); 856 const auto& maxwell3d = gpu.Maxwell3D();
@@ -788,8 +865,8 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
788 865
789 // Bind the uniform to the sampler. 866 // Bind the uniform to the sampler.
790 867
791 glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry), 868 glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
792 current_bindpoint); 869 shader->GetUniformLocation(entry), current_bindpoint);
793 870
794 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); 871 const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
795 872
@@ -862,12 +939,18 @@ void RasterizerOpenGL::SyncCullMode() {
862 } 939 }
863} 940}
864 941
865void RasterizerOpenGL::SyncDepthScale() { 942void RasterizerOpenGL::SyncPrimitiveRestart() {
866 UNREACHABLE(); 943 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
944
945 state.primitive_restart.enabled = regs.primitive_restart.enabled;
946 state.primitive_restart.index = regs.primitive_restart.index;
867} 947}
868 948
869void RasterizerOpenGL::SyncDepthOffset() { 949void RasterizerOpenGL::SyncDepthRange() {
870 UNREACHABLE(); 950 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
951
952 state.depth.depth_range_near = regs.viewport->depth_range_near;
953 state.depth.depth_range_far = regs.viewport->depth_range_far;
871} 954}
872 955
873void RasterizerOpenGL::SyncDepthTestState() { 956void RasterizerOpenGL::SyncDepthTestState() {
@@ -947,14 +1030,19 @@ void RasterizerOpenGL::SyncLogicOpState() {
947 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); 1030 state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
948} 1031}
949 1032
950void RasterizerOpenGL::SyncAlphaTest() { 1033void RasterizerOpenGL::SyncScissorTest() {
951 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 1034 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
952 1035
953 // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be 1036 state.scissor.enabled = (regs.scissor_test.enable != 0);
954 // implemented with a test+discard in fragment shaders. 1037 // TODO(Blinkhawk): Figure if the hardware supports scissor testing per viewport and how it's
955 if (regs.alpha_test_enabled != 0) { 1038 // implemented.
956 LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented"); 1039 if (regs.scissor_test.enable != 0) {
957 UNREACHABLE(); 1040 const u32 width = regs.scissor_test.max_x - regs.scissor_test.min_x;
1041 const u32 height = regs.scissor_test.max_y - regs.scissor_test.min_y;
1042 state.scissor.x = regs.scissor_test.min_x;
1043 state.scissor.y = regs.scissor_test.min_y;
1044 state.scissor.width = width;
1045 state.scissor.height = height;
958 } 1046 }
959} 1047}
960 1048
@@ -976,4 +1064,15 @@ void RasterizerOpenGL::SyncPointState() {
976 state.point.size = regs.point_size == 0 ? 1 : regs.point_size; 1064 state.point.size = regs.point_size == 0 ? 1 : regs.point_size;
977} 1065}
978 1066
1067void RasterizerOpenGL::CheckAlphaTests() {
1068 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
1069
1070 if (regs.alpha_test_enabled != 0 && regs.rt_control.count > 1) {
1071 LOG_CRITICAL(
1072 Render_OpenGL,
1073 "Alpha Testing is enabled with Multiple Render Targets, this behavior is undefined.");
1074 UNREACHABLE();
1075 }
1076}
1077
979} // namespace OpenGL 1078} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index bf954bb5d..47097c569 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -8,12 +8,12 @@
8#include <cstddef> 8#include <cstddef>
9#include <map> 9#include <map>
10#include <memory> 10#include <memory>
11#include <optional>
11#include <tuple> 12#include <tuple>
12#include <utility> 13#include <utility>
13#include <vector> 14#include <vector>
14 15
15#include <boost/icl/interval_map.hpp> 16#include <boost/icl/interval_map.hpp>
16#include <boost/optional.hpp>
17#include <boost/range/iterator_range.hpp> 17#include <boost/range/iterator_range.hpp>
18#include <glad/glad.h> 18#include <glad/glad.h>
19 19
@@ -52,8 +52,8 @@ public:
52 void FlushRegion(VAddr addr, u64 size) override; 52 void FlushRegion(VAddr addr, u64 size) override;
53 void InvalidateRegion(VAddr addr, u64 size) override; 53 void InvalidateRegion(VAddr addr, u64 size) override;
54 void FlushAndInvalidateRegion(VAddr addr, u64 size) override; 54 void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
55 bool AccelerateDisplayTransfer(const void* config) override; 55 bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
56 bool AccelerateTextureCopy(const void* config) override; 56 const Tegra::Engines::Fermi2D::Regs::Surface& dst) override;
57 bool AccelerateFill(const void* config) override; 57 bool AccelerateFill(const void* config) override;
58 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 58 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
59 u32 pixel_stride) override; 59 u32 pixel_stride) override;
@@ -93,9 +93,12 @@ private:
93 private: 93 private:
94 Tegra::Texture::TextureFilter mag_filter; 94 Tegra::Texture::TextureFilter mag_filter;
95 Tegra::Texture::TextureFilter min_filter; 95 Tegra::Texture::TextureFilter min_filter;
96 Tegra::Texture::TextureMipmapFilter mip_filter;
96 Tegra::Texture::WrapMode wrap_u; 97 Tegra::Texture::WrapMode wrap_u;
97 Tegra::Texture::WrapMode wrap_v; 98 Tegra::Texture::WrapMode wrap_v;
98 Tegra::Texture::WrapMode wrap_p; 99 Tegra::Texture::WrapMode wrap_p;
100 bool uses_depth_compare;
101 Tegra::Texture::DepthCompareFunc depth_compare_func;
99 GLvec4 border_color; 102 GLvec4 border_color;
100 }; 103 };
101 104
@@ -108,7 +111,7 @@ private:
108 */ 111 */
109 void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, 112 void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true,
110 bool preserve_contents = true, 113 bool preserve_contents = true,
111 boost::optional<std::size_t> single_color_target = {}); 114 std::optional<std::size_t> single_color_target = {});
112 115
113 /* 116 /*
114 * Configures the current constbuffers to use for the draw command. 117 * Configures the current constbuffers to use for the draw command.
@@ -118,7 +121,7 @@ private:
118 * @returns The next available bindpoint for use in the next shader stage. 121 * @returns The next available bindpoint for use in the next shader stage.
119 */ 122 */
120 u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 123 u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
121 u32 current_bindpoint); 124 GLenum primitive_mode, u32 current_bindpoint);
122 125
123 /* 126 /*
124 * Configures the current textures to use for the draw command. 127 * Configures the current textures to use for the draw command.
@@ -128,7 +131,7 @@ private:
128 * @returns The next available bindpoint for use in the next shader stage. 131 * @returns The next available bindpoint for use in the next shader stage.
129 */ 132 */
130 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader, 133 u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
131 u32 current_unit); 134 GLenum primitive_mode, u32 current_unit);
132 135
133 /// Syncs the viewport to match the guest state 136 /// Syncs the viewport to match the guest state
134 void SyncViewport(); 137 void SyncViewport();
@@ -142,11 +145,11 @@ private:
142 /// Syncs the cull mode to match the guest state 145 /// Syncs the cull mode to match the guest state
143 void SyncCullMode(); 146 void SyncCullMode();
144 147
145 /// Syncs the depth scale to match the guest state 148 /// Syncs the primitve restart to match the guest state
146 void SyncDepthScale(); 149 void SyncPrimitiveRestart();
147 150
148 /// Syncs the depth offset to match the guest state 151 /// Syncs the depth range to match the guest state
149 void SyncDepthOffset(); 152 void SyncDepthRange();
150 153
151 /// Syncs the depth test state to match the guest state 154 /// Syncs the depth test state to match the guest state
152 void SyncDepthTestState(); 155 void SyncDepthTestState();
@@ -160,8 +163,8 @@ private:
160 /// Syncs the LogicOp state to match the guest state 163 /// Syncs the LogicOp state to match the guest state
161 void SyncLogicOpState(); 164 void SyncLogicOpState();
162 165
163 /// Syncs the alpha test state to match the guest state 166 /// Syncs the scissor test state to match the guest state
164 void SyncAlphaTest(); 167 void SyncScissorTest();
165 168
166 /// Syncs the transform feedback state to match the guest state 169 /// Syncs the transform feedback state to match the guest state
167 void SyncTransformFeedback(); 170 void SyncTransformFeedback();
@@ -169,6 +172,9 @@ private:
169 /// Syncs the point state to match the guest state 172 /// Syncs the point state to match the guest state
170 void SyncPointState(); 173 void SyncPointState();
171 174
175 /// Check asserts for alpha testing.
176 void CheckAlphaTests();
177
172 bool has_ARB_direct_state_access = false; 178 bool has_ARB_direct_state_access = false;
173 bool has_ARB_multi_bind = false; 179 bool has_ARB_multi_bind = false;
174 bool has_ARB_separate_shader_objects = false; 180 bool has_ARB_separate_shader_objects = false;
@@ -205,7 +211,7 @@ private:
205 211
206 DrawParameters SetupDraw(); 212 DrawParameters SetupDraw();
207 213
208 void SetupShaders(); 214 void SetupShaders(GLenum primitive_mode);
209 215
210 enum class AccelDraw { Disabled, Arrays, Indexed }; 216 enum class AccelDraw { Disabled, Arrays, Indexed };
211 AccelDraw accelerate_draw = AccelDraw::Disabled; 217 AccelDraw accelerate_draw = AccelDraw::Disabled;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index ce967c4d6..0e11557c4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -16,15 +16,21 @@
16#include "core/settings.h" 16#include "core/settings.h"
17#include "video_core/engines/maxwell_3d.h" 17#include "video_core/engines/maxwell_3d.h"
18#include "video_core/renderer_opengl/gl_rasterizer_cache.h" 18#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
19#include "video_core/renderer_opengl/utils.h"
20#include "video_core/surface.h"
19#include "video_core/textures/astc.h" 21#include "video_core/textures/astc.h"
20#include "video_core/textures/decoders.h" 22#include "video_core/textures/decoders.h"
21#include "video_core/utils.h" 23#include "video_core/utils.h"
22 24
23namespace OpenGL { 25namespace OpenGL {
24 26
25using SurfaceType = SurfaceParams::SurfaceType; 27using VideoCore::Surface::ComponentTypeFromDepthFormat;
26using PixelFormat = SurfaceParams::PixelFormat; 28using VideoCore::Surface::ComponentTypeFromRenderTarget;
27using ComponentType = SurfaceParams::ComponentType; 29using VideoCore::Surface::ComponentTypeFromTexture;
30using VideoCore::Surface::PixelFormatFromDepthFormat;
31using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
32using VideoCore::Surface::PixelFormatFromTextureFormat;
33using VideoCore::Surface::SurfaceTargetFromTextureType;
28 34
29struct FormatTuple { 35struct FormatTuple {
30 GLint internal_format; 36 GLint internal_format;
@@ -34,20 +40,62 @@ struct FormatTuple {
34 bool compressed; 40 bool compressed;
35}; 41};
36 42
37static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { 43void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
38 auto& gpu{Core::System::GetInstance().GPU()}; 44 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
39 const auto cpu_addr{gpu.MemoryManager().GpuToCpuAddress(gpu_addr)}; 45 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
40 return cpu_addr ? *cpu_addr : 0; 46
47 addr = cpu_addr ? *cpu_addr : 0;
48 gpu_addr = gpu_addr_;
49 size_in_bytes = SizeInBytesRaw();
50
51 if (IsPixelFormatASTC(pixel_format)) {
52 // ASTC is uncompressed in software, in emulated as RGBA8
53 size_in_bytes_gl = width * height * depth * 4;
54 } else {
55 size_in_bytes_gl = SizeInBytesGL();
56 }
57}
58
59std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only,
60 bool uncompressed) const {
61 const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
62 const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
63 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
64 u32 m_depth = (layer_only ? 1U : depth);
65 u32 m_width = MipWidth(mip_level);
66 u32 m_height = MipHeight(mip_level);
67 m_width = uncompressed ? m_width : std::max(1U, (m_width + tile_x - 1) / tile_x);
68 m_height = uncompressed ? m_height : std::max(1U, (m_height + tile_y - 1) / tile_y);
69 m_depth = std::max(1U, m_depth >> mip_level);
70 u32 m_block_height = MipBlockHeight(mip_level);
71 u32 m_block_depth = MipBlockDepth(mip_level);
72 return Tegra::Texture::CalculateSize(force_gl ? false : is_tiled, bytes_per_pixel, m_width,
73 m_height, m_depth, m_block_height, m_block_depth);
74}
75
76std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
77 bool uncompressed) const {
78 std::size_t block_size_bytes = Tegra::Texture::GetGOBSize() * block_height * block_depth;
79 std::size_t size = 0;
80 for (u32 i = 0; i < max_mip_level; i++) {
81 size += InnerMipmapMemorySize(i, force_gl, layer_only, uncompressed);
82 }
83 if (!force_gl && is_tiled) {
84 size = Common::AlignUp(size, block_size_bytes);
85 }
86 return size;
41} 87}
42 88
43/*static*/ SurfaceParams SurfaceParams::CreateForTexture( 89/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
44 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { 90 const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
45 SurfaceParams params{}; 91 SurfaceParams params{};
46 params.addr = TryGetCpuAddr(config.tic.Address());
47 params.is_tiled = config.tic.IsTiled(); 92 params.is_tiled = config.tic.IsTiled();
93 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
48 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, 94 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
49 params.pixel_format = 95 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
50 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); 96 params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
97 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
98 params.srgb_conversion);
51 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); 99 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
52 params.type = GetFormatType(params.pixel_format); 100 params.type = GetFormatType(params.pixel_format);
53 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); 101 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
@@ -85,21 +133,27 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
85 break; 133 break;
86 } 134 }
87 135
88 params.size_in_bytes_total = params.SizeInBytesTotal(); 136 params.is_layered = SurfaceTargetIsLayered(params.target);
89 params.size_in_bytes_2d = params.SizeInBytes2D();
90 params.max_mip_level = config.tic.max_mip_level + 1; 137 params.max_mip_level = config.tic.max_mip_level + 1;
91 params.rt = {}; 138 params.rt = {};
92 139
140 params.InitCacheParameters(config.tic.Address());
141
93 return params; 142 return params;
94} 143}
95 144
96/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { 145/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {
97 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; 146 const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
98 SurfaceParams params{}; 147 SurfaceParams params{};
99 params.addr = TryGetCpuAddr(config.Address()); 148
100 params.is_tiled = true; 149 params.is_tiled =
101 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 150 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
151 params.block_width = 1 << config.memory_layout.block_width;
152 params.block_height = 1 << config.memory_layout.block_height;
153 params.block_depth = 1 << config.memory_layout.block_depth;
102 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); 154 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
155 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
156 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
103 params.component_type = ComponentTypeFromRenderTarget(config.format); 157 params.component_type = ComponentTypeFromRenderTarget(config.format);
104 params.type = GetFormatType(params.pixel_format); 158 params.type = GetFormatType(params.pixel_format);
105 params.width = config.width; 159 params.width = config.width;
@@ -107,43 +161,76 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
107 params.unaligned_height = config.height; 161 params.unaligned_height = config.height;
108 params.target = SurfaceTarget::Texture2D; 162 params.target = SurfaceTarget::Texture2D;
109 params.depth = 1; 163 params.depth = 1;
110 params.size_in_bytes_total = params.SizeInBytesTotal(); 164 params.max_mip_level = 1;
111 params.size_in_bytes_2d = params.SizeInBytes2D(); 165 params.is_layered = false;
112 params.max_mip_level = 0;
113 166
114 // Render target specific parameters, not used for caching 167 // Render target specific parameters, not used for caching
115 params.rt.index = static_cast<u32>(index); 168 params.rt.index = static_cast<u32>(index);
116 params.rt.array_mode = config.array_mode; 169 params.rt.array_mode = config.array_mode;
117 params.rt.layer_stride = config.layer_stride; 170 params.rt.layer_stride = config.layer_stride;
171 params.rt.volume = config.volume;
118 params.rt.base_layer = config.base_layer; 172 params.rt.base_layer = config.base_layer;
119 173
174 params.InitCacheParameters(config.Address());
175
120 return params; 176 return params;
121} 177}
122 178
123/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 179/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
124 Tegra::GPUVAddr zeta_address, 180 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
125 Tegra::DepthFormat format) { 181 u32 block_width, u32 block_height, u32 block_depth,
182 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
126 SurfaceParams params{}; 183 SurfaceParams params{};
127 params.addr = TryGetCpuAddr(zeta_address); 184
128 params.is_tiled = true; 185 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
129 params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight; 186 params.block_width = 1 << std::min(block_width, 5U);
187 params.block_height = 1 << std::min(block_height, 5U);
188 params.block_depth = 1 << std::min(block_depth, 5U);
130 params.pixel_format = PixelFormatFromDepthFormat(format); 189 params.pixel_format = PixelFormatFromDepthFormat(format);
131 params.component_type = ComponentTypeFromDepthFormat(format); 190 params.component_type = ComponentTypeFromDepthFormat(format);
132 params.type = GetFormatType(params.pixel_format); 191 params.type = GetFormatType(params.pixel_format);
192 params.srgb_conversion = false;
133 params.width = zeta_width; 193 params.width = zeta_width;
134 params.height = zeta_height; 194 params.height = zeta_height;
135 params.unaligned_height = zeta_height; 195 params.unaligned_height = zeta_height;
136 params.target = SurfaceTarget::Texture2D; 196 params.target = SurfaceTarget::Texture2D;
137 params.depth = 1; 197 params.depth = 1;
138 params.size_in_bytes_total = params.SizeInBytesTotal(); 198 params.max_mip_level = 1;
139 params.size_in_bytes_2d = params.SizeInBytes2D(); 199 params.is_layered = false;
140 params.max_mip_level = 0; 200 params.rt = {};
201
202 params.InitCacheParameters(zeta_address);
203
204 return params;
205}
206
207/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
208 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
209 SurfaceParams params{};
210
211 params.is_tiled = !config.linear;
212 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
213 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
214 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
215 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
216 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
217 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
218 params.component_type = ComponentTypeFromRenderTarget(config.format);
219 params.type = GetFormatType(params.pixel_format);
220 params.width = config.width;
221 params.height = config.height;
222 params.unaligned_height = config.height;
223 params.target = SurfaceTarget::Texture2D;
224 params.depth = 1;
225 params.max_mip_level = 1;
141 params.rt = {}; 226 params.rt = {};
142 227
228 params.InitCacheParameters(config.Address());
229
143 return params; 230 return params;
144} 231}
145 232
146static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ 233static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{
147 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U 234 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U
148 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S 235 {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S
149 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI 236 {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI
@@ -159,7 +246,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
159 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, 246 {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
160 false}, // R11FG11FB10F 247 false}, // R11FG11FB10F
161 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI 248 {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI
162 {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 249 {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
163 true}, // DXT1 250 true}, // DXT1
164 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, 251 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
165 true}, // DXT23 252 true}, // DXT23
@@ -193,12 +280,31 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
193 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I 280 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I
194 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S 281 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S
195 {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F 282 {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F
196 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 283 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm,
197 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U 284 false}, // RGBA8_SRGB
198 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S 285 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U
199 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 286 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
200 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 287 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
201 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 288 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
289 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
290 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
291 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
292 {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
293 // Compressed sRGB formats
294 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
295 true}, // DXT1_SRGB
296 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
297 true}, // DXT23_SRGB
298 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
299 true}, // DXT45_SRGB
300 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8,
301 ComponentType::UNorm, true}, // BC7U_SRGB
302 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB
303 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB
304 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB
305 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB
306 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5
307 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X5_SRGB
202 308
203 // Depth formats 309 // Depth formats
204 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F 310 {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F
@@ -214,19 +320,19 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
214 ComponentType::Float, false}, // Z32FS8 320 ComponentType::Float, false}, // Z32FS8
215}}; 321}};
216 322
217static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) { 323static GLenum SurfaceTargetToGL(SurfaceTarget target) {
218 switch (target) { 324 switch (target) {
219 case SurfaceParams::SurfaceTarget::Texture1D: 325 case SurfaceTarget::Texture1D:
220 return GL_TEXTURE_1D; 326 return GL_TEXTURE_1D;
221 case SurfaceParams::SurfaceTarget::Texture2D: 327 case SurfaceTarget::Texture2D:
222 return GL_TEXTURE_2D; 328 return GL_TEXTURE_2D;
223 case SurfaceParams::SurfaceTarget::Texture3D: 329 case SurfaceTarget::Texture3D:
224 return GL_TEXTURE_3D; 330 return GL_TEXTURE_3D;
225 case SurfaceParams::SurfaceTarget::Texture1DArray: 331 case SurfaceTarget::Texture1DArray:
226 return GL_TEXTURE_1D_ARRAY; 332 return GL_TEXTURE_1D_ARRAY;
227 case SurfaceParams::SurfaceTarget::Texture2DArray: 333 case SurfaceTarget::Texture2DArray:
228 return GL_TEXTURE_2D_ARRAY; 334 return GL_TEXTURE_2D_ARRAY;
229 case SurfaceParams::SurfaceTarget::TextureCubemap: 335 case SurfaceTarget::TextureCubemap:
230 return GL_TEXTURE_CUBE_MAP; 336 return GL_TEXTURE_CUBE_MAP;
231 } 337 }
232 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); 338 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
@@ -242,81 +348,44 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
242 return format; 348 return format;
243} 349}
244 350
245static bool IsPixelFormatASTC(PixelFormat format) { 351MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const {
246 switch (format) { 352 u32 actual_height{std::max(1U, unaligned_height >> mip_level)};
247 case PixelFormat::ASTC_2D_4X4:
248 case PixelFormat::ASTC_2D_8X8:
249 return true;
250 default:
251 return false;
252 }
253}
254
255static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
256 switch (format) {
257 case PixelFormat::ASTC_2D_4X4:
258 return {4, 4};
259 case PixelFormat::ASTC_2D_8X8:
260 return {8, 8};
261 default:
262 LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
263 UNREACHABLE();
264 }
265}
266
267MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
268 u32 actual_height{unaligned_height};
269 if (IsPixelFormatASTC(pixel_format)) { 353 if (IsPixelFormatASTC(pixel_format)) {
270 // ASTC formats must stop at the ATSC block size boundary 354 // ASTC formats must stop at the ATSC block size boundary
271 actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second); 355 actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second);
272 } 356 }
273 return {0, actual_height, width, 0}; 357 return {0, actual_height, MipWidth(mip_level), 0};
274}
275
276/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
277static bool IsFormatBCn(PixelFormat format) {
278 switch (format) {
279 case PixelFormat::DXT1:
280 case PixelFormat::DXT23:
281 case PixelFormat::DXT45:
282 case PixelFormat::DXN1:
283 case PixelFormat::DXN2SNORM:
284 case PixelFormat::DXN2UNORM:
285 case PixelFormat::BC7U:
286 case PixelFormat::BC6H_UF16:
287 case PixelFormat::BC6H_SF16:
288 return true;
289 }
290 return false;
291} 358}
292 359
293template <bool morton_to_gl, PixelFormat format> 360template <bool morton_to_gl, PixelFormat format>
294void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, 361void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer,
295 VAddr addr) { 362 std::size_t gl_buffer_size, VAddr addr) {
296 constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; 363 constexpr u32 bytes_per_pixel = GetBytesPerPixel(format);
297 constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); 364
365 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
366 // pixel values.
367 const u32 tile_size_x{GetDefaultBlockWidth(format)};
368 const u32 tile_size_y{GetDefaultBlockHeight(format)};
298 369
299 if (morton_to_gl) { 370 if (morton_to_gl) {
300 // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual 371 const std::vector<u8> data =
301 // pixel values. 372 Tegra::Texture::UnswizzleTexture(addr, tile_size_x, tile_size_y, bytes_per_pixel,
302 const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; 373 stride, height, depth, block_height, block_depth);
303 const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
304 addr, tile_size, bytes_per_pixel, stride, height, block_height);
305 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; 374 const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};
306 memcpy(gl_buffer, data.data(), size_to_copy); 375 memcpy(gl_buffer, data.data(), size_to_copy);
307 } else { 376 } else {
308 // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should 377 Tegra::Texture::CopySwizzledData((stride + tile_size_x - 1) / tile_size_x,
309 // check the configuration for this and perform more generic un/swizzle 378 (height + tile_size_y - 1) / tile_size_y, depth,
310 LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); 379 bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr),
311 VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, 380 gl_buffer, false, block_height, block_depth);
312 Memory::GetPointer(addr), gl_buffer, morton_to_gl);
313 } 381 }
314} 382}
315 383
316static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 384using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),
317 SurfaceParams::MaxPixelFormat> 385 VideoCore::Surface::MaxPixelFormat>;
318 morton_to_gl_fns = { 386
319 // clang-format off 387static constexpr GLConversionArray morton_to_gl_fns = {
388 // clang-format off
320 MortonCopy<true, PixelFormat::ABGR8U>, 389 MortonCopy<true, PixelFormat::ABGR8U>,
321 MortonCopy<true, PixelFormat::ABGR8S>, 390 MortonCopy<true, PixelFormat::ABGR8S>,
322 MortonCopy<true, PixelFormat::ABGR8UI>, 391 MortonCopy<true, PixelFormat::ABGR8UI>,
@@ -357,24 +426,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
357 MortonCopy<true, PixelFormat::RG16I>, 426 MortonCopy<true, PixelFormat::RG16I>,
358 MortonCopy<true, PixelFormat::RG16S>, 427 MortonCopy<true, PixelFormat::RG16S>,
359 MortonCopy<true, PixelFormat::RGB32F>, 428 MortonCopy<true, PixelFormat::RGB32F>,
360 MortonCopy<true, PixelFormat::SRGBA8>, 429 MortonCopy<true, PixelFormat::RGBA8_SRGB>,
361 MortonCopy<true, PixelFormat::RG8U>, 430 MortonCopy<true, PixelFormat::RG8U>,
362 MortonCopy<true, PixelFormat::RG8S>, 431 MortonCopy<true, PixelFormat::RG8S>,
363 MortonCopy<true, PixelFormat::RG32UI>, 432 MortonCopy<true, PixelFormat::RG32UI>,
364 MortonCopy<true, PixelFormat::R32UI>, 433 MortonCopy<true, PixelFormat::R32UI>,
365 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 434 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
435 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
436 MortonCopy<true, PixelFormat::ASTC_2D_5X4>,
437 MortonCopy<true, PixelFormat::BGRA8_SRGB>,
438 MortonCopy<true, PixelFormat::DXT1_SRGB>,
439 MortonCopy<true, PixelFormat::DXT23_SRGB>,
440 MortonCopy<true, PixelFormat::DXT45_SRGB>,
441 MortonCopy<true, PixelFormat::BC7U_SRGB>,
442 MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>,
443 MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>,
444 MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>,
445 MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>,
446 MortonCopy<true, PixelFormat::ASTC_2D_5X5>,
447 MortonCopy<true, PixelFormat::ASTC_2D_5X5_SRGB>,
366 MortonCopy<true, PixelFormat::Z32F>, 448 MortonCopy<true, PixelFormat::Z32F>,
367 MortonCopy<true, PixelFormat::Z16>, 449 MortonCopy<true, PixelFormat::Z16>,
368 MortonCopy<true, PixelFormat::Z24S8>, 450 MortonCopy<true, PixelFormat::Z24S8>,
369 MortonCopy<true, PixelFormat::S8Z24>, 451 MortonCopy<true, PixelFormat::S8Z24>,
370 MortonCopy<true, PixelFormat::Z32FS8>, 452 MortonCopy<true, PixelFormat::Z32FS8>,
371 // clang-format on 453 // clang-format on
372}; 454};
373 455
374static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), 456static constexpr GLConversionArray gl_to_morton_fns = {
375 SurfaceParams::MaxPixelFormat> 457 // clang-format off
376 gl_to_morton_fns = {
377 // clang-format off
378 MortonCopy<false, PixelFormat::ABGR8U>, 458 MortonCopy<false, PixelFormat::ABGR8U>,
379 MortonCopy<false, PixelFormat::ABGR8S>, 459 MortonCopy<false, PixelFormat::ABGR8S>,
380 MortonCopy<false, PixelFormat::ABGR8UI>, 460 MortonCopy<false, PixelFormat::ABGR8UI>,
@@ -388,17 +468,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
388 MortonCopy<false, PixelFormat::RGBA16UI>, 468 MortonCopy<false, PixelFormat::RGBA16UI>,
389 MortonCopy<false, PixelFormat::R11FG11FB10F>, 469 MortonCopy<false, PixelFormat::R11FG11FB10F>,
390 MortonCopy<false, PixelFormat::RGBA32UI>, 470 MortonCopy<false, PixelFormat::RGBA32UI>,
391 // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4 471 MortonCopy<false, PixelFormat::DXT1>,
392 // formats are not supported 472 MortonCopy<false, PixelFormat::DXT23>,
393 nullptr, 473 MortonCopy<false, PixelFormat::DXT45>,
394 nullptr, 474 MortonCopy<false, PixelFormat::DXN1>,
395 nullptr, 475 MortonCopy<false, PixelFormat::DXN2UNORM>,
396 nullptr, 476 MortonCopy<false, PixelFormat::DXN2SNORM>,
397 nullptr, 477 MortonCopy<false, PixelFormat::BC7U>,
398 nullptr, 478 MortonCopy<false, PixelFormat::BC6H_UF16>,
399 nullptr, 479 MortonCopy<false, PixelFormat::BC6H_SF16>,
400 nullptr, 480 // TODO(Subv): Swizzling ASTC formats are not supported
401 nullptr,
402 nullptr, 481 nullptr,
403 MortonCopy<false, PixelFormat::G8R8U>, 482 MortonCopy<false, PixelFormat::G8R8U>,
404 MortonCopy<false, PixelFormat::G8R8S>, 483 MortonCopy<false, PixelFormat::G8R8S>,
@@ -417,20 +496,62 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
417 MortonCopy<false, PixelFormat::RG16I>, 496 MortonCopy<false, PixelFormat::RG16I>,
418 MortonCopy<false, PixelFormat::RG16S>, 497 MortonCopy<false, PixelFormat::RG16S>,
419 MortonCopy<false, PixelFormat::RGB32F>, 498 MortonCopy<false, PixelFormat::RGB32F>,
420 MortonCopy<false, PixelFormat::SRGBA8>, 499 MortonCopy<false, PixelFormat::RGBA8_SRGB>,
421 MortonCopy<false, PixelFormat::RG8U>, 500 MortonCopy<false, PixelFormat::RG8U>,
422 MortonCopy<false, PixelFormat::RG8S>, 501 MortonCopy<false, PixelFormat::RG8S>,
423 MortonCopy<false, PixelFormat::RG32UI>, 502 MortonCopy<false, PixelFormat::RG32UI>,
424 MortonCopy<false, PixelFormat::R32UI>, 503 MortonCopy<false, PixelFormat::R32UI>,
425 nullptr, 504 nullptr,
505 nullptr,
506 nullptr,
507 MortonCopy<false, PixelFormat::BGRA8_SRGB>,
508 MortonCopy<false, PixelFormat::DXT1_SRGB>,
509 MortonCopy<false, PixelFormat::DXT23_SRGB>,
510 MortonCopy<false, PixelFormat::DXT45_SRGB>,
511 MortonCopy<false, PixelFormat::BC7U_SRGB>,
512 nullptr,
513 nullptr,
514 nullptr,
515 nullptr,
516 nullptr,
517 nullptr,
426 MortonCopy<false, PixelFormat::Z32F>, 518 MortonCopy<false, PixelFormat::Z32F>,
427 MortonCopy<false, PixelFormat::Z16>, 519 MortonCopy<false, PixelFormat::Z16>,
428 MortonCopy<false, PixelFormat::Z24S8>, 520 MortonCopy<false, PixelFormat::Z24S8>,
429 MortonCopy<false, PixelFormat::S8Z24>, 521 MortonCopy<false, PixelFormat::S8Z24>,
430 MortonCopy<false, PixelFormat::Z32FS8>, 522 MortonCopy<false, PixelFormat::Z32FS8>,
431 // clang-format on 523 // clang-format on
432}; 524};
433 525
526void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
527 std::vector<u8>& gl_buffer, u32 mip_level) {
528 u32 depth = params.MipDepth(mip_level);
529 if (params.target == SurfaceTarget::Texture2D) {
530 // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
531 depth = 1U;
532 }
533 if (params.is_layered) {
534 u64 offset = params.GetMipmapLevelOffset(mip_level);
535 u64 offset_gl = 0;
536 u64 layer_size = params.LayerMemorySize();
537 u64 gl_size = params.LayerSizeGL(mip_level);
538 for (u32 i = 0; i < params.depth; i++) {
539 functions[static_cast<std::size_t>(params.pixel_format)](
540 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
541 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), 1,
542 gl_buffer.data() + offset_gl, gl_size, params.addr + offset);
543 offset += layer_size;
544 offset_gl += gl_size;
545 }
546 } else {
547 u64 offset = params.GetMipmapLevelOffset(mip_level);
548 functions[static_cast<std::size_t>(params.pixel_format)](
549 params.MipWidth(mip_level), params.MipBlockHeight(mip_level),
550 params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(),
551 gl_buffer.size(), params.addr + offset);
552 }
553}
554
434static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, 555static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
435 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, 556 GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
436 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 557 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
@@ -444,19 +565,21 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
444 OpenGLState state; 565 OpenGLState state;
445 state.draw.read_framebuffer = read_fb_handle; 566 state.draw.read_framebuffer = read_fb_handle;
446 state.draw.draw_framebuffer = draw_fb_handle; 567 state.draw.draw_framebuffer = draw_fb_handle;
568 // Set sRGB enabled if the destination surfaces need it
569 state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
447 state.Apply(); 570 state.Apply();
448 571
449 u32 buffers{}; 572 u32 buffers{};
450 573
451 if (src_params.type == SurfaceType::ColorTexture) { 574 if (src_params.type == SurfaceType::ColorTexture) {
452 switch (src_params.target) { 575 switch (src_params.target) {
453 case SurfaceParams::SurfaceTarget::Texture2D: 576 case SurfaceTarget::Texture2D:
454 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 577 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
455 GL_TEXTURE_2D, src_surface->Texture().handle, 0); 578 GL_TEXTURE_2D, src_surface->Texture().handle, 0);
456 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 579 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
457 0, 0); 580 0, 0);
458 break; 581 break;
459 case SurfaceParams::SurfaceTarget::TextureCubemap: 582 case SurfaceTarget::TextureCubemap:
460 glFramebufferTexture2D( 583 glFramebufferTexture2D(
461 GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 584 GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
462 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 585 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
@@ -465,12 +588,12 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
465 GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 588 GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
466 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); 589 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
467 break; 590 break;
468 case SurfaceParams::SurfaceTarget::Texture2DArray: 591 case SurfaceTarget::Texture2DArray:
469 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 592 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
470 src_surface->Texture().handle, 0, 0); 593 src_surface->Texture().handle, 0, 0);
471 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); 594 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
472 break; 595 break;
473 case SurfaceParams::SurfaceTarget::Texture3D: 596 case SurfaceTarget::Texture3D:
474 glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, 597 glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
475 SurfaceTargetToGL(src_params.target), 598 SurfaceTargetToGL(src_params.target),
476 src_surface->Texture().handle, 0, 0); 599 src_surface->Texture().handle, 0, 0);
@@ -486,13 +609,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
486 } 609 }
487 610
488 switch (dst_params.target) { 611 switch (dst_params.target) {
489 case SurfaceParams::SurfaceTarget::Texture2D: 612 case SurfaceTarget::Texture2D:
490 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 613 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
491 GL_TEXTURE_2D, dst_surface->Texture().handle, 0); 614 GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
492 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 615 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
493 0, 0); 616 0, 0);
494 break; 617 break;
495 case SurfaceParams::SurfaceTarget::TextureCubemap: 618 case SurfaceTarget::TextureCubemap:
496 glFramebufferTexture2D( 619 glFramebufferTexture2D(
497 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 620 GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
498 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 621 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
@@ -501,13 +624,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
501 GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 624 GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
502 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); 625 static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
503 break; 626 break;
504 case SurfaceParams::SurfaceTarget::Texture2DArray: 627 case SurfaceTarget::Texture2DArray:
505 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 628 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
506 dst_surface->Texture().handle, 0, 0); 629 dst_surface->Texture().handle, 0, 0);
507 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); 630 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
508 break; 631 break;
509 632
510 case SurfaceParams::SurfaceTarget::Texture3D: 633 case SurfaceTarget::Texture3D:
511 glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, 634 glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
512 SurfaceTargetToGL(dst_params.target), 635 SurfaceTargetToGL(dst_params.target),
513 dst_surface->Texture().handle, 0, 0); 636 dst_surface->Texture().handle, 0, 0);
@@ -559,6 +682,18 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
559 return true; 682 return true;
560} 683}
561 684
685static void FastCopySurface(const Surface& src_surface, const Surface& dst_surface) {
686 const auto& src_params{src_surface->GetSurfaceParams()};
687 const auto& dst_params{dst_surface->GetSurfaceParams()};
688
689 const u32 width{std::min(src_params.width, dst_params.width)};
690 const u32 height{std::min(src_params.height, dst_params.height)};
691
692 glCopyImageSubData(src_surface->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0, 0,
693 0, dst_surface->Texture().handle, SurfaceTargetToGL(dst_params.target), 0, 0,
694 0, 0, width, height, 1);
695}
696
562static void CopySurface(const Surface& src_surface, const Surface& dst_surface, 697static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
563 GLuint copy_pbo_handle, GLenum src_attachment = 0, 698 GLuint copy_pbo_handle, GLenum src_attachment = 0,
564 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { 699 GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
@@ -570,22 +705,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
570 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type); 705 auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
571 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); 706 auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
572 707
573 std::size_t buffer_size = 708 std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);
574 std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total);
575 709
576 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); 710 glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
577 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); 711 glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
578 if (source_format.compressed) { 712 if (source_format.compressed) {
579 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, 713 glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
580 static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr); 714 static_cast<GLsizei>(src_params.size_in_bytes), nullptr);
581 } else { 715 } else {
582 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format, 716 glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format,
583 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total), 717 source_format.type, static_cast<GLsizei>(src_params.size_in_bytes),
584 nullptr); 718 nullptr);
585 } 719 }
586 // If the new texture is bigger than the previous one, we need to fill in the rest with data 720 // If the new texture is bigger than the previous one, we need to fill in the rest with data
587 // from the CPU. 721 // from the CPU.
588 if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) { 722 if (src_params.size_in_bytes < dst_params.size_in_bytes) {
589 // Upload the rest of the memory. 723 // Upload the rest of the memory.
590 if (dst_params.is_tiled) { 724 if (dst_params.is_tiled) {
591 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest 725 // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
@@ -595,12 +729,12 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
595 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " 729 LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
596 "reinterpretation but the texture is tiled."); 730 "reinterpretation but the texture is tiled.");
597 } 731 }
598 std::size_t remaining_size = 732 std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;
599 dst_params.size_in_bytes_total - src_params.size_in_bytes_total;
600 std::vector<u8> data(remaining_size); 733 std::vector<u8> data(remaining_size);
601 Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(), 734 std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes),
602 data.size()); 735 data.size());
603 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size, 736
737 glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,
604 data.data()); 738 data.data());
605 } 739 }
606 740
@@ -617,21 +751,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
617 UNREACHABLE(); 751 UNREACHABLE();
618 } else { 752 } else {
619 switch (dst_params.target) { 753 switch (dst_params.target) {
620 case SurfaceParams::SurfaceTarget::Texture1D: 754 case SurfaceTarget::Texture1D:
621 glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format, 755 glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format,
622 dest_format.type, nullptr); 756 dest_format.type, nullptr);
623 break; 757 break;
624 case SurfaceParams::SurfaceTarget::Texture2D: 758 case SurfaceTarget::Texture2D:
625 glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height, 759 glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height,
626 dest_format.format, dest_format.type, nullptr); 760 dest_format.format, dest_format.type, nullptr);
627 break; 761 break;
628 case SurfaceParams::SurfaceTarget::Texture3D: 762 case SurfaceTarget::Texture3D:
629 case SurfaceParams::SurfaceTarget::Texture2DArray: 763 case SurfaceTarget::Texture2DArray:
630 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height, 764 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
631 static_cast<GLsizei>(dst_params.depth), dest_format.format, 765 static_cast<GLsizei>(dst_params.depth), dest_format.format,
632 dest_format.type, nullptr); 766 dest_format.type, nullptr);
633 break; 767 break;
634 case SurfaceParams::SurfaceTarget::TextureCubemap: 768 case SurfaceTarget::TextureCubemap:
635 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 769 glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0,
636 static_cast<GLint>(cubemap_face), width, height, 1, 770 static_cast<GLint>(cubemap_face), width, height, 1,
637 dest_format.format, dest_format.type, nullptr); 771 dest_format.format, dest_format.type, nullptr);
@@ -646,7 +780,8 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
646} 780}
647 781
648CachedSurface::CachedSurface(const SurfaceParams& params) 782CachedSurface::CachedSurface(const SurfaceParams& params)
649 : params(params), gl_target(SurfaceTargetToGL(params.target)) { 783 : params(params), gl_target(SurfaceTargetToGL(params.target)),
784 cached_size_in_bytes(params.size_in_bytes) {
650 texture.Create(); 785 texture.Create();
651 const auto& rect{params.GetRect()}; 786 const auto& rect{params.GetRect()};
652 787
@@ -667,38 +802,57 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
667 if (!format_tuple.compressed) { 802 if (!format_tuple.compressed) {
668 // Only pre-create the texture for non-compressed textures. 803 // Only pre-create the texture for non-compressed textures.
669 switch (params.target) { 804 switch (params.target) {
670 case SurfaceParams::SurfaceTarget::Texture1D: 805 case SurfaceTarget::Texture1D:
671 glTexStorage1D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, 806 glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level,
672 rect.GetWidth()); 807 format_tuple.internal_format, rect.GetWidth());
673 break; 808 break;
674 case SurfaceParams::SurfaceTarget::Texture2D: 809 case SurfaceTarget::Texture2D:
675 case SurfaceParams::SurfaceTarget::TextureCubemap: 810 case SurfaceTarget::TextureCubemap:
676 glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, 811 glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level,
677 rect.GetWidth(), rect.GetHeight()); 812 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight());
678 break; 813 break;
679 case SurfaceParams::SurfaceTarget::Texture3D: 814 case SurfaceTarget::Texture3D:
680 case SurfaceParams::SurfaceTarget::Texture2DArray: 815 case SurfaceTarget::Texture2DArray:
681 glTexStorage3D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, 816 glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
682 rect.GetWidth(), rect.GetHeight(), params.depth); 817 format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
818 params.depth);
683 break; 819 break;
684 default: 820 default:
685 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 821 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
686 static_cast<u32>(params.target)); 822 static_cast<u32>(params.target));
687 UNREACHABLE(); 823 UNREACHABLE();
688 glTexStorage2D(GL_TEXTURE_2D, 1, format_tuple.internal_format, rect.GetWidth(), 824 glTexStorage2D(GL_TEXTURE_2D, params.max_mip_level, format_tuple.internal_format,
689 rect.GetHeight()); 825 rect.GetWidth(), rect.GetHeight());
690 } 826 }
691 } 827 }
692 828
693 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); 829 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
830 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAG_FILTER, GL_LINEAR);
694 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 831 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
695 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 832 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
833 glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL,
834 params.max_mip_level - 1);
835 if (params.max_mip_level == 1) {
836 glTexParameterf(SurfaceTargetToGL(params.target), GL_TEXTURE_LOD_BIAS, 1000.0);
837 }
838
839 LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
840 SurfaceParams::SurfaceTargetName(params.target));
841
842 // Clamp size to mapped GPU memory region
843 // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000
844 // R32F render buffer. We do not yet know if this is a game bug or something else, but this
845 // check is necessary to prevent flushing from overwriting unmapped memory.
696 846
697 VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, 847 auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
698 SurfaceParams::SurfaceTargetName(params.target)); 848 const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr};
849 if (cached_size_in_bytes > max_size) {
850 LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size);
851 cached_size_in_bytes = max_size;
852 }
699} 853}
700 854
701static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 855static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {
702 union S8Z24 { 856 union S8Z24 {
703 BitField<0, 24, u32> z24; 857 BitField<0, 24, u32> z24;
704 BitField<24, 8, u32> s8; 858 BitField<24, 8, u32> s8;
@@ -711,22 +865,29 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
711 }; 865 };
712 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); 866 static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size");
713 867
714 S8Z24 input_pixel{}; 868 S8Z24 s8z24_pixel{};
715 Z24S8 output_pixel{}; 869 Z24S8 z24s8_pixel{};
716 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; 870 constexpr auto bpp{GetBytesPerPixel(PixelFormat::S8Z24)};
717 for (std::size_t y = 0; y < height; ++y) { 871 for (std::size_t y = 0; y < height; ++y) {
718 for (std::size_t x = 0; x < width; ++x) { 872 for (std::size_t x = 0; x < width; ++x) {
719 const std::size_t offset{bpp * (y * width + x)}; 873 const std::size_t offset{bpp * (y * width + x)};
720 std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); 874 if (reverse) {
721 output_pixel.s8.Assign(input_pixel.s8); 875 std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8));
722 output_pixel.z24.Assign(input_pixel.z24); 876 s8z24_pixel.s8.Assign(z24s8_pixel.s8);
723 std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); 877 s8z24_pixel.z24.Assign(z24s8_pixel.z24);
878 std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24));
879 } else {
880 std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24));
881 z24s8_pixel.s8.Assign(s8z24_pixel.s8);
882 z24s8_pixel.z24.Assign(s8z24_pixel.z24);
883 std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8));
884 }
724 } 885 }
725 } 886 }
726} 887}
727 888
728static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 889static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
729 constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; 890 constexpr auto bpp{GetBytesPerPixel(PixelFormat::G8R8U)};
730 for (std::size_t y = 0; y < height; ++y) { 891 for (std::size_t y = 0; y < height; ++y) {
731 for (std::size_t x = 0; x < width; ++x) { 892 for (std::size_t x = 0; x < width; ++x) {
732 const std::size_t offset{bpp * (y * width + x)}; 893 const std::size_t offset{bpp * (y * width + x)};
@@ -743,20 +904,29 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) {
743 * typical desktop GPUs. 904 * typical desktop GPUs.
744 */ 905 */
745static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, 906static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
746 u32 width, u32 height) { 907 u32 width, u32 height, u32 depth) {
747 switch (pixel_format) { 908 switch (pixel_format) {
748 case PixelFormat::ASTC_2D_4X4: 909 case PixelFormat::ASTC_2D_4X4:
749 case PixelFormat::ASTC_2D_8X8: { 910 case PixelFormat::ASTC_2D_8X8:
911 case PixelFormat::ASTC_2D_8X5:
912 case PixelFormat::ASTC_2D_5X4:
913 case PixelFormat::ASTC_2D_5X5:
914 case PixelFormat::ASTC_2D_4X4_SRGB:
915 case PixelFormat::ASTC_2D_8X8_SRGB:
916 case PixelFormat::ASTC_2D_8X5_SRGB:
917 case PixelFormat::ASTC_2D_5X4_SRGB:
918 case PixelFormat::ASTC_2D_5X5_SRGB: {
750 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. 919 // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.
751 u32 block_width{}; 920 u32 block_width{};
752 u32 block_height{}; 921 u32 block_height{};
753 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format); 922 std::tie(block_width, block_height) = GetASTCBlockSize(pixel_format);
754 data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height); 923 data =
924 Tegra::Texture::ASTC::Decompress(data, width, height, depth, block_width, block_height);
755 break; 925 break;
756 } 926 }
757 case PixelFormat::S8Z24: 927 case PixelFormat::S8Z24:
758 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. 928 // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24.
759 ConvertS8Z24ToZ24S8(data, width, height); 929 ConvertS8Z24ToZ24S8(data, width, height, false);
760 break; 930 break;
761 931
762 case PixelFormat::G8R8U: 932 case PixelFormat::G8R8U:
@@ -767,79 +937,98 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
767 } 937 }
768} 938}
769 939
940/**
941 * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to
942 * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or
943 * with typical desktop GPUs.
944 */
945static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format,
946 u32 width, u32 height) {
947 switch (pixel_format) {
948 case PixelFormat::G8R8U:
949 case PixelFormat::G8R8S:
950 case PixelFormat::ASTC_2D_4X4:
951 case PixelFormat::ASTC_2D_8X8:
952 case PixelFormat::ASTC_2D_4X4_SRGB:
953 case PixelFormat::ASTC_2D_8X8_SRGB: {
954 LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented",
955 static_cast<u32>(pixel_format));
956 UNREACHABLE();
957 break;
958 }
959 case PixelFormat::S8Z24:
960 // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24.
961 ConvertS8Z24ToZ24S8(data, width, height, true);
962 break;
963 }
964}
965
770MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); 966MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
771void CachedSurface::LoadGLBuffer() { 967void CachedSurface::LoadGLBuffer() {
772 ASSERT(params.type != SurfaceType::Fill);
773
774 const u8* const texture_src_data = Memory::GetPointer(params.addr);
775
776 ASSERT(texture_src_data);
777
778 const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
779 const u32 copy_size = params.width * params.height * bytes_per_pixel;
780 const std::size_t total_size = copy_size * params.depth;
781
782 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); 968 MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
783 969 gl_buffer.resize(params.max_mip_level);
970 for (u32 i = 0; i < params.max_mip_level; i++)
971 gl_buffer[i].resize(params.GetMipmapSizeGL(i));
784 if (params.is_tiled) { 972 if (params.is_tiled) {
785 gl_buffer.resize(total_size); 973 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
786 974 params.block_width, static_cast<u32>(params.target));
787 // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do 975 for (u32 i = 0; i < params.max_mip_level; i++)
788 // this for 3D textures, etc. 976 SwizzleFunc(morton_to_gl_fns, params, gl_buffer[i], i);
789 switch (params.target) {
790 case SurfaceParams::SurfaceTarget::Texture2D:
791 // Pass impl. to the fallback code below
792 break;
793 case SurfaceParams::SurfaceTarget::Texture2DArray:
794 case SurfaceParams::SurfaceTarget::TextureCubemap:
795 for (std::size_t index = 0; index < params.depth; ++index) {
796 const std::size_t offset{index * copy_size};
797 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
798 params.width, params.block_height, params.height, gl_buffer.data() + offset,
799 copy_size, params.addr + offset);
800 }
801 break;
802 default:
803 LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
804 static_cast<u32>(params.target));
805 UNREACHABLE();
806 }
807
808 morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
809 params.width, params.block_height, params.height, gl_buffer.data(), copy_size,
810 params.addr);
811 } else { 977 } else {
812 const u8* const texture_src_data_end{texture_src_data + total_size}; 978 const auto texture_src_data{Memory::GetPointer(params.addr)};
813 gl_buffer.assign(texture_src_data, texture_src_data_end); 979 const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
980 gl_buffer[0].assign(texture_src_data, texture_src_data_end);
814 } 981 }
815 982 for (u32 i = 0; i < params.max_mip_level; i++)
816 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); 983 ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
984 params.MipHeight(i), params.MipDepth(i));
817} 985}
818 986
819MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); 987MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
820void CachedSurface::FlushGLBuffer() { 988void CachedSurface::FlushGLBuffer() {
821 ASSERT_MSG(false, "Unimplemented"); 989 MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
822}
823 990
824MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); 991 ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented");
825void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
826 if (params.type == SurfaceType::Fill)
827 return;
828 992
829 MICROPROFILE_SCOPE(OpenGL_TextureUL); 993 // OpenGL temporary buffer needs to be big enough to store raw texture size
994 gl_buffer.resize(1);
995 gl_buffer[0].resize(GetSizeInBytes());
830 996
831 ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height * 997 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
832 GetGLBytesPerPixel(params.pixel_format) * params.depth); 998 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
999 ASSERT(params.width * GetBytesPerPixel(params.pixel_format) % 4 == 0);
1000 glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
1001 ASSERT(!tuple.compressed);
1002 glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
1003 glGetTextureImage(texture.handle, 0, tuple.format, tuple.type,
1004 static_cast<GLsizei>(gl_buffer[0].size()), gl_buffer[0].data());
1005 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1006 ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width,
1007 params.height);
1008 ASSERT(params.type != SurfaceType::Fill);
1009 const u8* const texture_src_data = Memory::GetPointer(params.addr);
1010 ASSERT(texture_src_data);
1011 if (params.is_tiled) {
1012 ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",
1013 params.block_width, static_cast<u32>(params.target));
833 1014
834 const auto& rect{params.GetRect()}; 1015 SwizzleFunc(gl_to_morton_fns, params, gl_buffer[0], 0);
1016 } else {
1017 std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes());
1018 }
1019}
1020
1021void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
1022 GLuint draw_fb_handle) {
1023 const auto& rect{params.GetRect(mip_map)};
835 1024
836 // Load data from memory to the surface 1025 // Load data from memory to the surface
837 const GLint x0 = static_cast<GLint>(rect.left); 1026 const GLint x0 = static_cast<GLint>(rect.left);
838 const GLint y0 = static_cast<GLint>(rect.bottom); 1027 const GLint y0 = static_cast<GLint>(rect.bottom);
839 std::size_t buffer_offset = 1028 std::size_t buffer_offset =
840 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + 1029 static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) +
841 static_cast<std::size_t>(x0)) * 1030 static_cast<std::size_t>(x0)) *
842 GetGLBytesPerPixel(params.pixel_format); 1031 GetBytesPerPixel(params.pixel_format);
843 1032
844 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); 1033 const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
845 const GLuint target_tex = texture.handle; 1034 const GLuint target_tex = texture.handle;
@@ -855,88 +1044,116 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
855 cur_state.Apply(); 1044 cur_state.Apply();
856 1045
857 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT 1046 // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
858 ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); 1047 ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0);
859 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); 1048 glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map)));
860 1049
1050 GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false));
861 glActiveTexture(GL_TEXTURE0); 1051 glActiveTexture(GL_TEXTURE0);
862 if (tuple.compressed) { 1052 if (tuple.compressed) {
863 switch (params.target) { 1053 switch (params.target) {
864 case SurfaceParams::SurfaceTarget::Texture2D: 1054 case SurfaceTarget::Texture2D:
865 glCompressedTexImage2D( 1055 glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
866 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 1056 static_cast<GLsizei>(params.MipWidth(mip_map)),
867 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, 1057 static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size,
868 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 1058 &gl_buffer[mip_map][buffer_offset]);
1059 break;
1060 case SurfaceTarget::Texture3D:
1061 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
1062 static_cast<GLsizei>(params.MipWidth(mip_map)),
1063 static_cast<GLsizei>(params.MipHeight(mip_map)),
1064 static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size,
1065 &gl_buffer[mip_map][buffer_offset]);
869 break; 1066 break;
870 case SurfaceParams::SurfaceTarget::Texture3D: 1067 case SurfaceTarget::Texture2DArray:
871 case SurfaceParams::SurfaceTarget::Texture2DArray: 1068 glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format,
872 glCompressedTexImage3D( 1069 static_cast<GLsizei>(params.MipWidth(mip_map)),
873 SurfaceTargetToGL(params.target), 0, tuple.internal_format, 1070 static_cast<GLsizei>(params.MipHeight(mip_map)),
874 static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 1071 static_cast<GLsizei>(params.depth), 0, image_size,
875 static_cast<GLsizei>(params.depth), 0, 1072 &gl_buffer[mip_map][buffer_offset]);
876 static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]);
877 break; 1073 break;
878 case SurfaceParams::SurfaceTarget::TextureCubemap: 1074 case SurfaceTarget::TextureCubemap: {
1075 GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map));
879 for (std::size_t face = 0; face < params.depth; ++face) { 1076 for (std::size_t face = 0; face < params.depth; ++face) {
880 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 1077 glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
881 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1078 mip_map, tuple.internal_format,
882 static_cast<GLsizei>(params.height), 0, 1079 static_cast<GLsizei>(params.MipWidth(mip_map)),
883 static_cast<GLsizei>(params.size_in_bytes_2d), 1080 static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
884 &gl_buffer[buffer_offset]); 1081 layer_size, &gl_buffer[mip_map][buffer_offset]);
885 buffer_offset += params.size_in_bytes_2d; 1082 buffer_offset += layer_size;
886 } 1083 }
887 break; 1084 break;
1085 }
888 default: 1086 default:
889 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1087 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
890 static_cast<u32>(params.target)); 1088 static_cast<u32>(params.target));
891 UNREACHABLE(); 1089 UNREACHABLE();
892 glCompressedTexImage2D( 1090 glCompressedTexImage2D(GL_TEXTURE_2D, mip_map, tuple.internal_format,
893 GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 1091 static_cast<GLsizei>(params.MipWidth(mip_map)),
894 static_cast<GLsizei>(params.height), 0, 1092 static_cast<GLsizei>(params.MipHeight(mip_map)), 0,
895 static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); 1093 static_cast<GLsizei>(params.size_in_bytes_gl),
1094 &gl_buffer[mip_map][buffer_offset]);
896 } 1095 }
897 } else { 1096 } else {
898 1097
899 switch (params.target) { 1098 switch (params.target) {
900 case SurfaceParams::SurfaceTarget::Texture1D: 1099 case SurfaceTarget::Texture1D:
901 glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0, 1100 glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0,
902 static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, 1101 static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
903 &gl_buffer[buffer_offset]); 1102 &gl_buffer[mip_map][buffer_offset]);
904 break; 1103 break;
905 case SurfaceParams::SurfaceTarget::Texture2D: 1104 case SurfaceTarget::Texture2D:
906 glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0, 1105 glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0,
907 static_cast<GLsizei>(rect.GetWidth()), 1106 static_cast<GLsizei>(rect.GetWidth()),
908 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1107 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
909 &gl_buffer[buffer_offset]); 1108 &gl_buffer[mip_map][buffer_offset]);
1109 break;
1110 case SurfaceTarget::Texture3D:
1111 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
1112 static_cast<GLsizei>(rect.GetWidth()),
1113 static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map),
1114 tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]);
910 break; 1115 break;
911 case SurfaceParams::SurfaceTarget::Texture3D: 1116 case SurfaceTarget::Texture2DArray:
912 case SurfaceParams::SurfaceTarget::Texture2DArray: 1117 glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0,
913 glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0,
914 static_cast<GLsizei>(rect.GetWidth()), 1118 static_cast<GLsizei>(rect.GetWidth()),
915 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, 1119 static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
916 tuple.type, &gl_buffer[buffer_offset]); 1120 tuple.type, &gl_buffer[mip_map][buffer_offset]);
917 break; 1121 break;
918 case SurfaceParams::SurfaceTarget::TextureCubemap: 1122 case SurfaceTarget::TextureCubemap: {
1123 std::size_t start = buffer_offset;
919 for (std::size_t face = 0; face < params.depth; ++face) { 1124 for (std::size_t face = 0; face < params.depth; ++face) {
920 glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, x0, 1125 glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map,
921 y0, static_cast<GLsizei>(rect.GetWidth()), 1126 x0, y0, static_cast<GLsizei>(rect.GetWidth()),
922 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1127 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
923 &gl_buffer[buffer_offset]); 1128 &gl_buffer[mip_map][buffer_offset]);
924 buffer_offset += params.size_in_bytes_2d; 1129 buffer_offset += params.LayerSizeGL(mip_map);
925 } 1130 }
926 break; 1131 break;
1132 }
927 default: 1133 default:
928 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1134 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
929 static_cast<u32>(params.target)); 1135 static_cast<u32>(params.target));
930 UNREACHABLE(); 1136 UNREACHABLE();
931 glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), 1137 glTexSubImage2D(GL_TEXTURE_2D, mip_map, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
932 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, 1138 static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
933 &gl_buffer[buffer_offset]); 1139 &gl_buffer[mip_map][buffer_offset]);
934 } 1140 }
935 } 1141 }
936 1142
937 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 1143 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
938} 1144}
939 1145
1146MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
1147void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
1148 if (params.type == SurfaceType::Fill)
1149 return;
1150
1151 MICROPROFILE_SCOPE(OpenGL_TextureUL);
1152
1153 for (u32 i = 0; i < params.max_mip_level; i++)
1154 UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle);
1155}
1156
940RasterizerCacheOpenGL::RasterizerCacheOpenGL() { 1157RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
941 read_framebuffer.Create(); 1158 read_framebuffer.Create();
942 draw_framebuffer.Create(); 1159 draw_framebuffer.Create();
@@ -955,7 +1172,9 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
955 } 1172 }
956 1173
957 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer( 1174 SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
958 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)}; 1175 regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format,
1176 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
1177 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
959 1178
960 return GetSurface(depth_params, preserve_contents); 1179 return GetSurface(depth_params, preserve_contents);
961} 1180}
@@ -981,10 +1200,7 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre
981void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { 1200void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
982 surface->LoadGLBuffer(); 1201 surface->LoadGLBuffer();
983 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); 1202 surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
984} 1203 surface->MarkAsModified(false, *this);
985
986void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
987 surface->FlushGLBuffer();
988} 1204}
989 1205
990Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { 1206Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) {
@@ -1001,8 +1217,8 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
1001 } else if (preserve_contents) { 1217 } else if (preserve_contents) {
1002 // If surface parameters changed and we care about keeping the previous data, recreate 1218 // If surface parameters changed and we care about keeping the previous data, recreate
1003 // the surface from the old one 1219 // the surface from the old one
1004 Unregister(surface);
1005 Surface new_surface{RecreateSurface(surface, params)}; 1220 Surface new_surface{RecreateSurface(surface, params)};
1221 Unregister(surface);
1006 Register(new_surface); 1222 Register(new_surface);
1007 return new_surface; 1223 return new_surface;
1008 } else { 1224 } else {
@@ -1033,6 +1249,34 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
1033 return surface; 1249 return surface;
1034} 1250}
1035 1251
1252void RasterizerCacheOpenGL::FermiCopySurface(
1253 const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
1254 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
1255
1256 const auto& src_params = SurfaceParams::CreateForFermiCopySurface(src_config);
1257 const auto& dst_params = SurfaceParams::CreateForFermiCopySurface(dst_config);
1258
1259 ASSERT(src_params.width == dst_params.width);
1260 ASSERT(src_params.height == dst_params.height);
1261 ASSERT(src_params.pixel_format == dst_params.pixel_format);
1262 ASSERT(src_params.block_height == dst_params.block_height);
1263 ASSERT(src_params.is_tiled == dst_params.is_tiled);
1264 ASSERT(src_params.depth == dst_params.depth);
1265 ASSERT(src_params.depth == 1); // Currently, FastCopySurface only works with 2D surfaces
1266 ASSERT(src_params.target == dst_params.target);
1267 ASSERT(src_params.rt.index == dst_params.rt.index);
1268
1269 FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
1270}
1271
1272void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
1273 const Surface& dst_surface) {
1274 const auto& src_params{src_surface->GetSurfaceParams()};
1275 const auto& dst_params{dst_surface->GetSurfaceParams()};
1276 FlushRegion(src_params.addr, dst_params.MemorySize());
1277 LoadSurface(dst_surface);
1278}
1279
1036Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, 1280Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1037 const SurfaceParams& new_params) { 1281 const SurfaceParams& new_params) {
1038 // Verify surface is compatible for blitting 1282 // Verify surface is compatible for blitting
@@ -1041,57 +1285,39 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
1041 // Get a new surface with the new parameters, and blit the previous surface to it 1285 // Get a new surface with the new parameters, and blit the previous surface to it
1042 Surface new_surface{GetUncachedSurface(new_params)}; 1286 Surface new_surface{GetUncachedSurface(new_params)};
1043 1287
1288 // With use_accurate_gpu_emulation enabled, do an accurate surface copy
1289 if (Settings::values.use_accurate_gpu_emulation) {
1290 AccurateCopySurface(old_surface, new_surface);
1291 return new_surface;
1292 }
1293
1294 // For compatible surfaces, we can just do fast glCopyImageSubData based copy
1295 if (old_params.target == new_params.target && old_params.type == new_params.type &&
1296 old_params.depth == new_params.depth && old_params.depth == 1 &&
1297 GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) {
1298 FastCopySurface(old_surface, new_surface);
1299 return new_surface;
1300 }
1301
1044 // If the format is the same, just do a framebuffer blit. This is significantly faster than 1302 // If the format is the same, just do a framebuffer blit. This is significantly faster than
1045 // using PBOs. The is also likely less accurate, as textures will be converted rather than 1303 // using PBOs. The is also likely less accurate, as textures will be converted rather than
1046 // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate 1304 // reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate
1047 // surface copy, where pixels are reinterpreted as a new format (without conversion). This 1305 // surface copy, where pixels are reinterpreted as a new format (without conversion). This
1048 // code path uses OpenGL PBOs and is quite slow. 1306 // code path uses OpenGL PBOs and is quite slow.
1049 const bool is_blit{old_params.pixel_format == new_params.pixel_format || 1307 const bool is_blit{old_params.pixel_format == new_params.pixel_format};
1050 !Settings::values.use_accurate_framebuffers};
1051 1308
1052 switch (new_params.target) { 1309 switch (new_params.target) {
1053 case SurfaceParams::SurfaceTarget::Texture2D: 1310 case SurfaceTarget::Texture2D:
1054 if (is_blit) { 1311 if (is_blit) {
1055 BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle); 1312 BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle);
1056 } else { 1313 } else {
1057 CopySurface(old_surface, new_surface, copy_pbo.handle); 1314 CopySurface(old_surface, new_surface, copy_pbo.handle);
1058 } 1315 }
1059 break; 1316 break;
1060 case SurfaceParams::SurfaceTarget::TextureCubemap: { 1317 case SurfaceTarget::TextureCubemap:
1061 if (old_params.rt.array_mode != 1) { 1318 case SurfaceTarget::Texture3D:
1062 // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this 1319 AccurateCopySurface(old_surface, new_surface);
1063 // yet (array rendering used as a cubemap texture).
1064 LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode);
1065 UNREACHABLE();
1066 return new_surface;
1067 }
1068
1069 // This seems to be used for render-to-cubemap texture
1070 ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected");
1071 ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected");
1072 ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented");
1073
1074 // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels.
1075 // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild.
1076 const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)};
1077
1078 for (std::size_t index = 0; index < new_params.depth; ++index) {
1079 Surface face_surface{TryGetReservedSurface(old_params)};
1080 ASSERT_MSG(face_surface, "Unexpected");
1081
1082 if (is_blit) {
1083 BlitSurface(face_surface, new_surface, read_framebuffer.handle,
1084 draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index,
1085 new_params.rt.index, index);
1086 } else {
1087 CopySurface(face_surface, new_surface, copy_pbo.handle,
1088 face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index);
1089 }
1090
1091 old_params.addr += byte_stride;
1092 }
1093 break; 1320 break;
1094 }
1095 default: 1321 default:
1096 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", 1322 LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
1097 static_cast<u32>(new_params.target)); 1323 static_cast<u32>(new_params.target));
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 49025a3fe..46ad37897 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -7,16 +7,20 @@
7#include <array> 7#include <array>
8#include <map> 8#include <map>
9#include <memory> 9#include <memory>
10#include <string>
10#include <vector> 11#include <vector>
11 12
12#include "common/alignment.h" 13#include "common/alignment.h"
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "common/hash.h" 15#include "common/hash.h"
15#include "common/math_util.h" 16#include "common/math_util.h"
17#include "video_core/engines/fermi_2d.h"
16#include "video_core/engines/maxwell_3d.h" 18#include "video_core/engines/maxwell_3d.h"
17#include "video_core/rasterizer_cache.h" 19#include "video_core/rasterizer_cache.h"
18#include "video_core/renderer_opengl/gl_resource_manager.h" 20#include "video_core/renderer_opengl/gl_resource_manager.h"
19#include "video_core/renderer_opengl/gl_shader_gen.h" 21#include "video_core/renderer_opengl/gl_shader_gen.h"
22#include "video_core/surface.h"
23#include "video_core/textures/decoders.h"
20#include "video_core/textures/texture.h" 24#include "video_core/textures/texture.h"
21 25
22namespace OpenGL { 26namespace OpenGL {
@@ -25,122 +29,12 @@ class CachedSurface;
25using Surface = std::shared_ptr<CachedSurface>; 29using Surface = std::shared_ptr<CachedSurface>;
26using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; 30using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
27 31
28struct SurfaceParams { 32using SurfaceTarget = VideoCore::Surface::SurfaceTarget;
29 enum class PixelFormat { 33using SurfaceType = VideoCore::Surface::SurfaceType;
30 ABGR8U = 0, 34using PixelFormat = VideoCore::Surface::PixelFormat;
31 ABGR8S = 1, 35using ComponentType = VideoCore::Surface::ComponentType;
32 ABGR8UI = 2,
33 B5G6R5U = 3,
34 A2B10G10R10U = 4,
35 A1B5G5R5U = 5,
36 R8U = 6,
37 R8UI = 7,
38 RGBA16F = 8,
39 RGBA16U = 9,
40 RGBA16UI = 10,
41 R11FG11FB10F = 11,
42 RGBA32UI = 12,
43 DXT1 = 13,
44 DXT23 = 14,
45 DXT45 = 15,
46 DXN1 = 16, // This is also known as BC4
47 DXN2UNORM = 17,
48 DXN2SNORM = 18,
49 BC7U = 19,
50 BC6H_UF16 = 20,
51 BC6H_SF16 = 21,
52 ASTC_2D_4X4 = 22,
53 G8R8U = 23,
54 G8R8S = 24,
55 BGRA8 = 25,
56 RGBA32F = 26,
57 RG32F = 27,
58 R32F = 28,
59 R16F = 29,
60 R16U = 30,
61 R16S = 31,
62 R16UI = 32,
63 R16I = 33,
64 RG16 = 34,
65 RG16F = 35,
66 RG16UI = 36,
67 RG16I = 37,
68 RG16S = 38,
69 RGB32F = 39,
70 SRGBA8 = 40,
71 RG8U = 41,
72 RG8S = 42,
73 RG32UI = 43,
74 R32UI = 44,
75 ASTC_2D_8X8 = 45,
76
77 MaxColorFormat,
78
79 // Depth formats
80 Z32F = 46,
81 Z16 = 47,
82
83 MaxDepthFormat,
84
85 // DepthStencil formats
86 Z24S8 = 48,
87 S8Z24 = 49,
88 Z32FS8 = 50,
89
90 MaxDepthStencilFormat,
91
92 Max = MaxDepthStencilFormat,
93 Invalid = 255,
94 };
95
96 static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);
97
98 enum class ComponentType {
99 Invalid = 0,
100 SNorm = 1,
101 UNorm = 2,
102 SInt = 3,
103 UInt = 4,
104 Float = 5,
105 };
106
107 enum class SurfaceType {
108 ColorTexture = 0,
109 Depth = 1,
110 DepthStencil = 2,
111 Fill = 3,
112 Invalid = 4,
113 };
114
115 enum class SurfaceTarget {
116 Texture1D,
117 Texture2D,
118 Texture3D,
119 Texture1DArray,
120 Texture2DArray,
121 TextureCubemap,
122 };
123
124 static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
125 switch (texture_type) {
126 case Tegra::Texture::TextureType::Texture1D:
127 return SurfaceTarget::Texture1D;
128 case Tegra::Texture::TextureType::Texture2D:
129 case Tegra::Texture::TextureType::Texture2DNoMipmap:
130 return SurfaceTarget::Texture2D;
131 case Tegra::Texture::TextureType::TextureCubemap:
132 return SurfaceTarget::TextureCubemap;
133 case Tegra::Texture::TextureType::Texture1DArray:
134 return SurfaceTarget::Texture1DArray;
135 case Tegra::Texture::TextureType::Texture2DArray:
136 return SurfaceTarget::Texture2DArray;
137 default:
138 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
139 UNREACHABLE();
140 return SurfaceTarget::Texture2D;
141 }
142 }
143 36
37struct SurfaceParams {
144 static std::string SurfaceTargetName(SurfaceTarget target) { 38 static std::string SurfaceTargetName(SurfaceTarget target) {
145 switch (target) { 39 switch (target) {
146 case SurfaceTarget::Texture1D: 40 case SurfaceTarget::Texture1D:
@@ -162,549 +56,123 @@ struct SurfaceParams {
162 } 56 }
163 } 57 }
164 58
165 /** 59 u32 GetFormatBpp() const {
166 * Gets the compression factor for the specified PixelFormat. This applies to just the 60 return VideoCore::Surface::GetFormatBpp(pixel_format);
167 * "compressed width" and "compressed height", not the overall compression factor of a
168 * compressed image. This is used for maintaining proper surface sizes for compressed
169 * texture formats.
170 */
171 static constexpr u32 GetCompressionFactor(PixelFormat format) {
172 if (format == PixelFormat::Invalid)
173 return 0;
174
175 constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
176 1, // ABGR8U
177 1, // ABGR8S
178 1, // ABGR8UI
179 1, // B5G6R5U
180 1, // A2B10G10R10U
181 1, // A1B5G5R5U
182 1, // R8U
183 1, // R8UI
184 1, // RGBA16F
185 1, // RGBA16U
186 1, // RGBA16UI
187 1, // R11FG11FB10F
188 1, // RGBA32UI
189 4, // DXT1
190 4, // DXT23
191 4, // DXT45
192 4, // DXN1
193 4, // DXN2UNORM
194 4, // DXN2SNORM
195 4, // BC7U
196 4, // BC6H_UF16
197 4, // BC6H_SF16
198 4, // ASTC_2D_4X4
199 1, // G8R8U
200 1, // G8R8S
201 1, // BGRA8
202 1, // RGBA32F
203 1, // RG32F
204 1, // R32F
205 1, // R16F
206 1, // R16U
207 1, // R16S
208 1, // R16UI
209 1, // R16I
210 1, // RG16
211 1, // RG16F
212 1, // RG16UI
213 1, // RG16I
214 1, // RG16S
215 1, // RGB32F
216 1, // SRGBA8
217 1, // RG8U
218 1, // RG8S
219 1, // RG32UI
220 1, // R32UI
221 4, // ASTC_2D_8X8
222 1, // Z32F
223 1, // Z16
224 1, // Z24S8
225 1, // S8Z24
226 1, // Z32FS8
227 }};
228
229 ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
230 return compression_factor_table[static_cast<std::size_t>(format)];
231 } 61 }
232 62
233 static constexpr u32 GetFormatBpp(PixelFormat format) { 63 /// Returns the rectangle corresponding to this surface
234 if (format == PixelFormat::Invalid) 64 MathUtil::Rectangle<u32> GetRect(u32 mip_level = 0) const;
235 return 0;
236
237 constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
238 32, // ABGR8U
239 32, // ABGR8S
240 32, // ABGR8UI
241 16, // B5G6R5U
242 32, // A2B10G10R10U
243 16, // A1B5G5R5U
244 8, // R8U
245 8, // R8UI
246 64, // RGBA16F
247 64, // RGBA16U
248 64, // RGBA16UI
249 32, // R11FG11FB10F
250 128, // RGBA32UI
251 64, // DXT1
252 128, // DXT23
253 128, // DXT45
254 64, // DXN1
255 128, // DXN2UNORM
256 128, // DXN2SNORM
257 128, // BC7U
258 128, // BC6H_UF16
259 128, // BC6H_SF16
260 32, // ASTC_2D_4X4
261 16, // G8R8U
262 16, // G8R8S
263 32, // BGRA8
264 128, // RGBA32F
265 64, // RG32F
266 32, // R32F
267 16, // R16F
268 16, // R16U
269 16, // R16S
270 16, // R16UI
271 16, // R16I
272 32, // RG16
273 32, // RG16F
274 32, // RG16UI
275 32, // RG16I
276 32, // RG16S
277 96, // RGB32F
278 32, // SRGBA8
279 16, // RG8U
280 16, // RG8S
281 64, // RG32UI
282 32, // R32UI
283 16, // ASTC_2D_8X8
284 32, // Z32F
285 16, // Z16
286 32, // Z24S8
287 32, // S8Z24
288 64, // Z32FS8
289 }};
290
291 ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
292 return bpp_table[static_cast<std::size_t>(format)];
293 }
294 65
295 u32 GetFormatBpp() const { 66 /// Returns the total size of this surface in bytes, adjusted for compression
296 return GetFormatBpp(pixel_format); 67 std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {
297 } 68 const u32 compression_factor{GetCompressionFactor(pixel_format)};
69 const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)};
70 const size_t uncompressed_size{
71 Tegra::Texture::CalculateSize((ignore_tiled ? false : is_tiled), bytes_per_pixel, width,
72 height, depth, block_height, block_depth)};
298 73
299 static PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { 74 // Divide by compression_factor^2, as height and width are factored by this
300 switch (format) { 75 return uncompressed_size / (compression_factor * compression_factor);
301 case Tegra::DepthFormat::S8_Z24_UNORM:
302 return PixelFormat::S8Z24;
303 case Tegra::DepthFormat::Z24_S8_UNORM:
304 return PixelFormat::Z24S8;
305 case Tegra::DepthFormat::Z32_FLOAT:
306 return PixelFormat::Z32F;
307 case Tegra::DepthFormat::Z16_UNORM:
308 return PixelFormat::Z16;
309 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
310 return PixelFormat::Z32FS8;
311 default:
312 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
313 UNREACHABLE();
314 }
315 } 76 }
316 77
317 static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { 78 /// Returns the size of this surface as an OpenGL texture in bytes
318 switch (format) { 79 std::size_t SizeInBytesGL() const {
319 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the 80 return SizeInBytesRaw(true);
320 // gamma.
321 case Tegra::RenderTargetFormat::RGBA8_SRGB:
322 case Tegra::RenderTargetFormat::RGBA8_UNORM:
323 return PixelFormat::ABGR8U;
324 case Tegra::RenderTargetFormat::RGBA8_SNORM:
325 return PixelFormat::ABGR8S;
326 case Tegra::RenderTargetFormat::RGBA8_UINT:
327 return PixelFormat::ABGR8UI;
328 case Tegra::RenderTargetFormat::BGRA8_SRGB:
329 case Tegra::RenderTargetFormat::BGRA8_UNORM:
330 return PixelFormat::BGRA8;
331 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
332 return PixelFormat::A2B10G10R10U;
333 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
334 return PixelFormat::RGBA16F;
335 case Tegra::RenderTargetFormat::RGBA16_UNORM:
336 return PixelFormat::RGBA16U;
337 case Tegra::RenderTargetFormat::RGBA16_UINT:
338 return PixelFormat::RGBA16UI;
339 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
340 return PixelFormat::RGBA32F;
341 case Tegra::RenderTargetFormat::RG32_FLOAT:
342 return PixelFormat::RG32F;
343 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
344 return PixelFormat::R11FG11FB10F;
345 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
346 return PixelFormat::B5G6R5U;
347 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
348 return PixelFormat::A1B5G5R5U;
349 case Tegra::RenderTargetFormat::RGBA32_UINT:
350 return PixelFormat::RGBA32UI;
351 case Tegra::RenderTargetFormat::R8_UNORM:
352 return PixelFormat::R8U;
353 case Tegra::RenderTargetFormat::R8_UINT:
354 return PixelFormat::R8UI;
355 case Tegra::RenderTargetFormat::RG16_FLOAT:
356 return PixelFormat::RG16F;
357 case Tegra::RenderTargetFormat::RG16_UINT:
358 return PixelFormat::RG16UI;
359 case Tegra::RenderTargetFormat::RG16_SINT:
360 return PixelFormat::RG16I;
361 case Tegra::RenderTargetFormat::RG16_UNORM:
362 return PixelFormat::RG16;
363 case Tegra::RenderTargetFormat::RG16_SNORM:
364 return PixelFormat::RG16S;
365 case Tegra::RenderTargetFormat::RG8_UNORM:
366 return PixelFormat::RG8U;
367 case Tegra::RenderTargetFormat::RG8_SNORM:
368 return PixelFormat::RG8S;
369 case Tegra::RenderTargetFormat::R16_FLOAT:
370 return PixelFormat::R16F;
371 case Tegra::RenderTargetFormat::R16_UNORM:
372 return PixelFormat::R16U;
373 case Tegra::RenderTargetFormat::R16_SNORM:
374 return PixelFormat::R16S;
375 case Tegra::RenderTargetFormat::R16_UINT:
376 return PixelFormat::R16UI;
377 case Tegra::RenderTargetFormat::R16_SINT:
378 return PixelFormat::R16I;
379 case Tegra::RenderTargetFormat::R32_FLOAT:
380 return PixelFormat::R32F;
381 case Tegra::RenderTargetFormat::R32_UINT:
382 return PixelFormat::R32UI;
383 case Tegra::RenderTargetFormat::RG32_UINT:
384 return PixelFormat::RG32UI;
385 default:
386 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
387 UNREACHABLE();
388 }
389 } 81 }
390 82
391 static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, 83 /// Returns the size of this surface as a cube face in bytes
392 Tegra::Texture::ComponentType component_type) { 84 std::size_t SizeInBytesCubeFace() const {
393 // TODO(Subv): Properly implement this 85 return size_in_bytes / 6;
394 switch (format) {
395 case Tegra::Texture::TextureFormat::A8R8G8B8:
396 switch (component_type) {
397 case Tegra::Texture::ComponentType::UNORM:
398 return PixelFormat::ABGR8U;
399 case Tegra::Texture::ComponentType::SNORM:
400 return PixelFormat::ABGR8S;
401 case Tegra::Texture::ComponentType::UINT:
402 return PixelFormat::ABGR8UI;
403 }
404 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
405 static_cast<u32>(component_type));
406 UNREACHABLE();
407 case Tegra::Texture::TextureFormat::B5G6R5:
408 switch (component_type) {
409 case Tegra::Texture::ComponentType::UNORM:
410 return PixelFormat::B5G6R5U;
411 }
412 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
413 static_cast<u32>(component_type));
414 UNREACHABLE();
415 case Tegra::Texture::TextureFormat::A2B10G10R10:
416 switch (component_type) {
417 case Tegra::Texture::ComponentType::UNORM:
418 return PixelFormat::A2B10G10R10U;
419 }
420 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
421 static_cast<u32>(component_type));
422 UNREACHABLE();
423 case Tegra::Texture::TextureFormat::A1B5G5R5:
424 switch (component_type) {
425 case Tegra::Texture::ComponentType::UNORM:
426 return PixelFormat::A1B5G5R5U;
427 }
428 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
429 static_cast<u32>(component_type));
430 UNREACHABLE();
431 case Tegra::Texture::TextureFormat::R8:
432 switch (component_type) {
433 case Tegra::Texture::ComponentType::UNORM:
434 return PixelFormat::R8U;
435 case Tegra::Texture::ComponentType::UINT:
436 return PixelFormat::R8UI;
437 }
438 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
439 static_cast<u32>(component_type));
440 UNREACHABLE();
441 case Tegra::Texture::TextureFormat::G8R8:
442 switch (component_type) {
443 case Tegra::Texture::ComponentType::UNORM:
444 return PixelFormat::G8R8U;
445 case Tegra::Texture::ComponentType::SNORM:
446 return PixelFormat::G8R8S;
447 }
448 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
449 static_cast<u32>(component_type));
450 UNREACHABLE();
451 case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
452 switch (component_type) {
453 case Tegra::Texture::ComponentType::UNORM:
454 return PixelFormat::RGBA16U;
455 case Tegra::Texture::ComponentType::FLOAT:
456 return PixelFormat::RGBA16F;
457 }
458 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
459 static_cast<u32>(component_type));
460 UNREACHABLE();
461 case Tegra::Texture::TextureFormat::BF10GF11RF11:
462 switch (component_type) {
463 case Tegra::Texture::ComponentType::FLOAT:
464 return PixelFormat::R11FG11FB10F;
465 }
466 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
467 static_cast<u32>(component_type));
468 UNREACHABLE();
469 case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
470 switch (component_type) {
471 case Tegra::Texture::ComponentType::FLOAT:
472 return PixelFormat::RGBA32F;
473 case Tegra::Texture::ComponentType::UINT:
474 return PixelFormat::RGBA32UI;
475 }
476 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
477 static_cast<u32>(component_type));
478 UNREACHABLE();
479 case Tegra::Texture::TextureFormat::R32_G32:
480 switch (component_type) {
481 case Tegra::Texture::ComponentType::FLOAT:
482 return PixelFormat::RG32F;
483 case Tegra::Texture::ComponentType::UINT:
484 return PixelFormat::RG32UI;
485 }
486 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
487 static_cast<u32>(component_type));
488 UNREACHABLE();
489 case Tegra::Texture::TextureFormat::R32_G32_B32:
490 switch (component_type) {
491 case Tegra::Texture::ComponentType::FLOAT:
492 return PixelFormat::RGB32F;
493 }
494 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
495 static_cast<u32>(component_type));
496 UNREACHABLE();
497 case Tegra::Texture::TextureFormat::R16:
498 switch (component_type) {
499 case Tegra::Texture::ComponentType::FLOAT:
500 return PixelFormat::R16F;
501 case Tegra::Texture::ComponentType::UNORM:
502 return PixelFormat::R16U;
503 case Tegra::Texture::ComponentType::SNORM:
504 return PixelFormat::R16S;
505 case Tegra::Texture::ComponentType::UINT:
506 return PixelFormat::R16UI;
507 case Tegra::Texture::ComponentType::SINT:
508 return PixelFormat::R16I;
509 }
510 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
511 static_cast<u32>(component_type));
512 UNREACHABLE();
513 case Tegra::Texture::TextureFormat::R32:
514 switch (component_type) {
515 case Tegra::Texture::ComponentType::FLOAT:
516 return PixelFormat::R32F;
517 case Tegra::Texture::ComponentType::UINT:
518 return PixelFormat::R32UI;
519 }
520 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
521 static_cast<u32>(component_type));
522 UNREACHABLE();
523 case Tegra::Texture::TextureFormat::ZF32:
524 return PixelFormat::Z32F;
525 case Tegra::Texture::TextureFormat::Z16:
526 return PixelFormat::Z16;
527 case Tegra::Texture::TextureFormat::Z24S8:
528 return PixelFormat::Z24S8;
529 case Tegra::Texture::TextureFormat::DXT1:
530 return PixelFormat::DXT1;
531 case Tegra::Texture::TextureFormat::DXT23:
532 return PixelFormat::DXT23;
533 case Tegra::Texture::TextureFormat::DXT45:
534 return PixelFormat::DXT45;
535 case Tegra::Texture::TextureFormat::DXN1:
536 return PixelFormat::DXN1;
537 case Tegra::Texture::TextureFormat::DXN2:
538 switch (component_type) {
539 case Tegra::Texture::ComponentType::UNORM:
540 return PixelFormat::DXN2UNORM;
541 case Tegra::Texture::ComponentType::SNORM:
542 return PixelFormat::DXN2SNORM;
543 }
544 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
545 static_cast<u32>(component_type));
546 UNREACHABLE();
547 case Tegra::Texture::TextureFormat::BC7U:
548 return PixelFormat::BC7U;
549 case Tegra::Texture::TextureFormat::BC6H_UF16:
550 return PixelFormat::BC6H_UF16;
551 case Tegra::Texture::TextureFormat::BC6H_SF16:
552 return PixelFormat::BC6H_SF16;
553 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
554 return PixelFormat::ASTC_2D_4X4;
555 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
556 return PixelFormat::ASTC_2D_8X8;
557 case Tegra::Texture::TextureFormat::R16_G16:
558 switch (component_type) {
559 case Tegra::Texture::ComponentType::FLOAT:
560 return PixelFormat::RG16F;
561 case Tegra::Texture::ComponentType::UNORM:
562 return PixelFormat::RG16;
563 case Tegra::Texture::ComponentType::SNORM:
564 return PixelFormat::RG16S;
565 case Tegra::Texture::ComponentType::UINT:
566 return PixelFormat::RG16UI;
567 case Tegra::Texture::ComponentType::SINT:
568 return PixelFormat::RG16I;
569 }
570 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
571 static_cast<u32>(component_type));
572 UNREACHABLE();
573 default:
574 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}",
575 static_cast<u32>(format), static_cast<u32>(component_type));
576 UNREACHABLE();
577 }
578 } 86 }
579 87
580 static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { 88 /// Returns the size of this surface as an OpenGL cube face in bytes
581 // TODO(Subv): Implement more component types 89 std::size_t SizeInBytesCubeFaceGL() const {
582 switch (type) { 90 return size_in_bytes_gl / 6;
583 case Tegra::Texture::ComponentType::UNORM:
584 return ComponentType::UNorm;
585 case Tegra::Texture::ComponentType::FLOAT:
586 return ComponentType::Float;
587 case Tegra::Texture::ComponentType::SNORM:
588 return ComponentType::SNorm;
589 case Tegra::Texture::ComponentType::UINT:
590 return ComponentType::UInt;
591 case Tegra::Texture::ComponentType::SINT:
592 return ComponentType::SInt;
593 default:
594 LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
595 UNREACHABLE();
596 }
597 } 91 }
598 92
599 static ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) { 93 /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps.
600 // TODO(Subv): Implement more render targets 94 std::size_t MemorySize() const {
601 switch (format) { 95 std::size_t size = InnerMemorySize(false, is_layered);
602 case Tegra::RenderTargetFormat::RGBA8_UNORM: 96 if (is_layered)
603 case Tegra::RenderTargetFormat::RGBA8_SRGB: 97 return size * depth;
604 case Tegra::RenderTargetFormat::BGRA8_UNORM: 98 return size;
605 case Tegra::RenderTargetFormat::BGRA8_SRGB:
606 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
607 case Tegra::RenderTargetFormat::R8_UNORM:
608 case Tegra::RenderTargetFormat::RG16_UNORM:
609 case Tegra::RenderTargetFormat::R16_UNORM:
610 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
611 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
612 case Tegra::RenderTargetFormat::RG8_UNORM:
613 case Tegra::RenderTargetFormat::RGBA16_UNORM:
614 return ComponentType::UNorm;
615 case Tegra::RenderTargetFormat::RGBA8_SNORM:
616 case Tegra::RenderTargetFormat::RG16_SNORM:
617 case Tegra::RenderTargetFormat::R16_SNORM:
618 case Tegra::RenderTargetFormat::RG8_SNORM:
619 return ComponentType::SNorm;
620 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
621 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
622 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
623 case Tegra::RenderTargetFormat::RG32_FLOAT:
624 case Tegra::RenderTargetFormat::RG16_FLOAT:
625 case Tegra::RenderTargetFormat::R16_FLOAT:
626 case Tegra::RenderTargetFormat::R32_FLOAT:
627 return ComponentType::Float;
628 case Tegra::RenderTargetFormat::RGBA32_UINT:
629 case Tegra::RenderTargetFormat::RGBA16_UINT:
630 case Tegra::RenderTargetFormat::RG16_UINT:
631 case Tegra::RenderTargetFormat::R8_UINT:
632 case Tegra::RenderTargetFormat::R16_UINT:
633 case Tegra::RenderTargetFormat::RG32_UINT:
634 case Tegra::RenderTargetFormat::R32_UINT:
635 case Tegra::RenderTargetFormat::RGBA8_UINT:
636 return ComponentType::UInt;
637 case Tegra::RenderTargetFormat::RG16_SINT:
638 case Tegra::RenderTargetFormat::R16_SINT:
639 return ComponentType::SInt;
640 default:
641 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
642 UNREACHABLE();
643 }
644 } 99 }
645 100
646 static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { 101 /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including
647 switch (format) { 102 /// mipmaps.
648 case Tegra::FramebufferConfig::PixelFormat::ABGR8: 103 std::size_t LayerMemorySize() const {
649 return PixelFormat::ABGR8U; 104 return InnerMemorySize(false, true);
650 default:
651 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
652 UNREACHABLE();
653 }
654 } 105 }
655 106
656 static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) { 107 /// Returns the size of a layer of this surface in OpenGL.
657 switch (format) { 108 std::size_t LayerSizeGL(u32 mip_level) const {
658 case Tegra::DepthFormat::Z16_UNORM: 109 return InnerMipmapMemorySize(mip_level, true, is_layered, false);
659 case Tegra::DepthFormat::S8_Z24_UNORM:
660 case Tegra::DepthFormat::Z24_S8_UNORM:
661 return ComponentType::UNorm;
662 case Tegra::DepthFormat::Z32_FLOAT:
663 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
664 return ComponentType::Float;
665 default:
666 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
667 UNREACHABLE();
668 }
669 } 110 }
670 111
671 static SurfaceType GetFormatType(PixelFormat pixel_format) { 112 std::size_t GetMipmapSizeGL(u32 mip_level, bool ignore_compressed = true) const {
672 if (static_cast<std::size_t>(pixel_format) < 113 std::size_t size = InnerMipmapMemorySize(mip_level, true, is_layered, ignore_compressed);
673 static_cast<std::size_t>(PixelFormat::MaxColorFormat)) { 114 if (is_layered)
674 return SurfaceType::ColorTexture; 115 return size * depth;
675 } 116 return size;
117 }
676 118
677 if (static_cast<std::size_t>(pixel_format) < 119 std::size_t GetMipmapLevelOffset(u32 mip_level) const {
678 static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) { 120 std::size_t offset = 0;
679 return SurfaceType::Depth; 121 for (u32 i = 0; i < mip_level; i++)
680 } 122 offset += InnerMipmapMemorySize(i, false, is_layered);
123 return offset;
124 }
681 125
682 if (static_cast<std::size_t>(pixel_format) < 126 std::size_t GetMipmapLevelOffsetGL(u32 mip_level) const {
683 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { 127 std::size_t offset = 0;
684 return SurfaceType::DepthStencil; 128 for (u32 i = 0; i < mip_level; i++)
685 } 129 offset += InnerMipmapMemorySize(i, true, is_layered);
130 return offset;
131 }
686 132
687 // TODO(Subv): Implement the other formats 133 u32 MipWidth(u32 mip_level) const {
688 ASSERT(false); 134 return std::max(1U, width >> mip_level);
135 }
689 136
690 return SurfaceType::Invalid; 137 u32 MipHeight(u32 mip_level) const {
138 return std::max(1U, height >> mip_level);
691 } 139 }
692 140
693 /// Returns the rectangle corresponding to this surface 141 u32 MipDepth(u32 mip_level) const {
694 MathUtil::Rectangle<u32> GetRect() const; 142 return is_layered ? depth : std::max(1U, depth >> mip_level);
143 }
695 144
696 /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression 145 // Auto block resizing algorithm from:
697 std::size_t SizeInBytes2D() const { 146 // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
698 const u32 compression_factor{GetCompressionFactor(pixel_format)}; 147 u32 MipBlockHeight(u32 mip_level) const {
699 ASSERT(width % compression_factor == 0); 148 if (mip_level == 0)
700 ASSERT(height % compression_factor == 0); 149 return block_height;
701 return (width / compression_factor) * (height / compression_factor) * 150 u32 alt_height = MipHeight(mip_level);
702 GetFormatBpp(pixel_format) / CHAR_BIT; 151 u32 h = GetDefaultBlockHeight(pixel_format);
152 u32 blocks_in_y = (alt_height + h - 1) / h;
153 u32 bh = 16;
154 while (bh > 1 && blocks_in_y <= bh * 4) {
155 bh >>= 1;
156 }
157 return bh;
703 } 158 }
704 159
705 /// Returns the total size of this surface in bytes, adjusted for compression 160 u32 MipBlockDepth(u32 mip_level) const {
706 std::size_t SizeInBytesTotal() const { 161 if (mip_level == 0)
707 return SizeInBytes2D() * depth; 162 return block_depth;
163 if (is_layered)
164 return 1;
165 u32 depth = MipDepth(mip_level);
166 u32 bd = 32;
167 while (bd > 1 && depth * 2 <= bd) {
168 bd >>= 1;
169 }
170 if (bd == 32) {
171 u32 bh = MipBlockHeight(mip_level);
172 if (bh >= 4)
173 return 16;
174 }
175 return bd;
708 } 176 }
709 177
710 /// Creates SurfaceParams from a texture configuration 178 /// Creates SurfaceParams from a texture configuration
@@ -715,9 +183,14 @@ struct SurfaceParams {
715 static SurfaceParams CreateForFramebuffer(std::size_t index); 183 static SurfaceParams CreateForFramebuffer(std::size_t index);
716 184
717 /// Creates SurfaceParams for a depth buffer configuration 185 /// Creates SurfaceParams for a depth buffer configuration
718 static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, 186 static SurfaceParams CreateForDepthBuffer(
719 Tegra::GPUVAddr zeta_address, 187 u32 zeta_width, u32 zeta_height, Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format,
720 Tegra::DepthFormat format); 188 u32 block_width, u32 block_height, u32 block_depth,
189 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
190
191 /// Creates SurfaceParams for a Fermi2D surface copy
192 static SurfaceParams CreateForFermiCopySurface(
193 const Tegra::Engines::Fermi2D::Regs::Surface& config);
721 194
722 /// Checks if surfaces are compatible for caching 195 /// Checks if surfaces are compatible for caching
723 bool IsCompatibleSurface(const SurfaceParams& other) const { 196 bool IsCompatibleSurface(const SurfaceParams& other) const {
@@ -726,9 +199,13 @@ struct SurfaceParams {
726 other.depth); 199 other.depth);
727 } 200 }
728 201
729 VAddr addr; 202 /// Initializes parameters for caching, should be called after everything has been initialized
203 void InitCacheParameters(Tegra::GPUVAddr gpu_addr);
204
730 bool is_tiled; 205 bool is_tiled;
206 u32 block_width;
731 u32 block_height; 207 u32 block_height;
208 u32 block_depth;
732 PixelFormat pixel_format; 209 PixelFormat pixel_format;
733 ComponentType component_type; 210 ComponentType component_type;
734 SurfaceType type; 211 SurfaceType type;
@@ -736,18 +213,30 @@ struct SurfaceParams {
736 u32 height; 213 u32 height;
737 u32 depth; 214 u32 depth;
738 u32 unaligned_height; 215 u32 unaligned_height;
739 std::size_t size_in_bytes_total;
740 std::size_t size_in_bytes_2d;
741 SurfaceTarget target; 216 SurfaceTarget target;
742 u32 max_mip_level; 217 u32 max_mip_level;
218 bool is_layered;
219 bool srgb_conversion;
220 // Parameters used for caching
221 VAddr addr;
222 Tegra::GPUVAddr gpu_addr;
223 std::size_t size_in_bytes;
224 std::size_t size_in_bytes_gl;
743 225
744 // Render target specific parameters, not used in caching 226 // Render target specific parameters, not used in caching
745 struct { 227 struct {
746 u32 index; 228 u32 index;
747 u32 array_mode; 229 u32 array_mode;
230 u32 volume;
748 u32 layer_stride; 231 u32 layer_stride;
749 u32 base_layer; 232 u32 base_layer;
750 } rt; 233 } rt;
234
235private:
236 std::size_t InnerMipmapMemorySize(u32 mip_level, bool force_gl = false, bool layer_only = false,
237 bool uncompressed = false) const;
238 std::size_t InnerMemorySize(bool force_gl = false, bool layer_only = false,
239 bool uncompressed = false) const;
751}; 240};
752 241
753}; // namespace OpenGL 242}; // namespace OpenGL
@@ -757,7 +246,8 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
757 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) { 246 static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
758 SurfaceReserveKey res; 247 SurfaceReserveKey res;
759 res.state = params; 248 res.state = params;
760 res.state.rt = {}; // Ignore rt config in caching 249 res.state.gpu_addr = {}; // Ignore GPU vaddr in caching
250 res.state.rt = {}; // Ignore rt config in caching
761 return res; 251 return res;
762 } 252 }
763}; 253};
@@ -772,16 +262,20 @@ struct hash<SurfaceReserveKey> {
772 262
773namespace OpenGL { 263namespace OpenGL {
774 264
775class CachedSurface final { 265class CachedSurface final : public RasterizerCacheObject {
776public: 266public:
777 CachedSurface(const SurfaceParams& params); 267 CachedSurface(const SurfaceParams& params);
778 268
779 VAddr GetAddr() const { 269 VAddr GetAddr() const override {
780 return params.addr; 270 return params.addr;
781 } 271 }
782 272
783 std::size_t GetSizeInBytes() const { 273 std::size_t GetSizeInBytes() const override {
784 return params.size_in_bytes_total; 274 return cached_size_in_bytes;
275 }
276
277 void Flush() override {
278 FlushGLBuffer();
785 } 279 }
786 280
787 const OGLTexture& Texture() const { 281 const OGLTexture& Texture() const {
@@ -792,13 +286,6 @@ public:
792 return gl_target; 286 return gl_target;
793 } 287 }
794 288
795 static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
796 if (format == SurfaceParams::PixelFormat::Invalid)
797 return 0;
798
799 return SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
800 }
801
802 const SurfaceParams& GetSurfaceParams() const { 289 const SurfaceParams& GetSurfaceParams() const {
803 return params; 290 return params;
804 } 291 }
@@ -811,10 +298,13 @@ public:
811 void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); 298 void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
812 299
813private: 300private:
301 void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle);
302
814 OGLTexture texture; 303 OGLTexture texture;
815 std::vector<u8> gl_buffer; 304 std::vector<std::vector<u8>> gl_buffer;
816 SurfaceParams params; 305 SurfaceParams params;
817 GLenum gl_target; 306 GLenum gl_target;
307 std::size_t cached_size_in_bytes;
818}; 308};
819 309
820class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { 310class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -831,12 +321,13 @@ public:
831 /// Get the color surface based on the framebuffer configuration and the specified render target 321 /// Get the color surface based on the framebuffer configuration and the specified render target
832 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); 322 Surface GetColorBufferSurface(std::size_t index, bool preserve_contents);
833 323
834 /// Flushes the surface to Switch memory
835 void FlushSurface(const Surface& surface);
836
837 /// Tries to find a framebuffer using on the provided CPU address 324 /// Tries to find a framebuffer using on the provided CPU address
838 Surface TryFindFramebufferSurface(VAddr addr) const; 325 Surface TryFindFramebufferSurface(VAddr addr) const;
839 326
327 /// Copies the contents of one surface to another
328 void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
329 const Tegra::Engines::Fermi2D::Regs::Surface& dst_config);
330
840private: 331private:
841 void LoadSurface(const Surface& surface); 332 void LoadSurface(const Surface& surface);
842 Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true); 333 Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true);
@@ -853,6 +344,9 @@ private:
853 /// Tries to get a reserved surface for the specified parameters 344 /// Tries to get a reserved surface for the specified parameters
854 Surface TryGetReservedSurface(const SurfaceParams& params); 345 Surface TryGetReservedSurface(const SurfaceParams& params);
855 346
347 /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data
348 void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface);
349
856 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have 350 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
857 /// previously been used. This is to prevent surfaces from being constantly created and 351 /// previously been used. This is to prevent surfaces from being constantly created and
858 /// destroyed when used with different surface parameters. 352 /// destroyed when used with different surface parameters.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7cd8f91e4..9522fd344 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -8,6 +8,7 @@
8#include "video_core/engines/maxwell_3d.h" 8#include "video_core/engines/maxwell_3d.h"
9#include "video_core/renderer_opengl/gl_shader_cache.h" 9#include "video_core/renderer_opengl/gl_shader_cache.h"
10#include "video_core/renderer_opengl/gl_shader_manager.h" 10#include "video_core/renderer_opengl/gl_shader_manager.h"
11#include "video_core/renderer_opengl/utils.h"
11#include "video_core/utils.h" 12#include "video_core/utils.h"
12 13
13namespace OpenGL { 14namespace OpenGL {
@@ -68,6 +69,10 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
68 program_result = GLShader::GenerateVertexShader(setup); 69 program_result = GLShader::GenerateVertexShader(setup);
69 gl_type = GL_VERTEX_SHADER; 70 gl_type = GL_VERTEX_SHADER;
70 break; 71 break;
72 case Maxwell::ShaderProgram::Geometry:
73 program_result = GLShader::GenerateGeometryShader(setup);
74 gl_type = GL_GEOMETRY_SHADER;
75 break;
71 case Maxwell::ShaderProgram::Fragment: 76 case Maxwell::ShaderProgram::Fragment:
72 program_result = GLShader::GenerateFragmentShader(setup); 77 program_result = GLShader::GenerateFragmentShader(setup);
73 gl_type = GL_FRAGMENT_SHADER; 78 gl_type = GL_FRAGMENT_SHADER;
@@ -80,11 +85,16 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
80 85
81 entries = program_result.second; 86 entries = program_result.second;
82 87
83 OGLShader shader; 88 if (program_type != Maxwell::ShaderProgram::Geometry) {
84 shader.Create(program_result.first.c_str(), gl_type); 89 OGLShader shader;
85 program.Create(true, shader.handle); 90 shader.Create(program_result.first.c_str(), gl_type);
86 SetShaderUniformBlockBindings(program.handle); 91 program.Create(true, shader.handle);
87 VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); 92 SetShaderUniformBlockBindings(program.handle);
93 LabelGLObject(GL_PROGRAM, program.handle, addr);
94 } else {
95 // Store shader's code to lazily build it on draw
96 geometry_programs.code = program_result.first;
97 }
88} 98}
89 99
90GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { 100GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
@@ -110,6 +120,21 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
110 return search->second; 120 return search->second;
111} 121}
112 122
123GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
124 const std::string& glsl_topology,
125 const std::string& debug_name) {
126 if (target_program.handle != 0) {
127 return target_program.handle;
128 }
129 const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
130 OGLShader shader;
131 shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
132 target_program.Create(true, shader.handle);
133 SetShaderUniformBlockBindings(target_program.handle);
134 LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
135 return target_program.handle;
136};
137
113Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { 138Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
114 const VAddr program_addr{GetShaderAddress(program)}; 139 const VAddr program_addr{GetShaderAddress(program)};
115 140
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 9bafe43a9..a210f1731 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -7,6 +7,7 @@
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9 9
10#include "common/assert.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "video_core/rasterizer_cache.h" 12#include "video_core/rasterizer_cache.h"
12#include "video_core/renderer_opengl/gl_resource_manager.h" 13#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -18,28 +19,52 @@ class CachedShader;
18using Shader = std::shared_ptr<CachedShader>; 19using Shader = std::shared_ptr<CachedShader>;
19using Maxwell = Tegra::Engines::Maxwell3D::Regs; 20using Maxwell = Tegra::Engines::Maxwell3D::Regs;
20 21
21class CachedShader final { 22class CachedShader final : public RasterizerCacheObject {
22public: 23public:
23 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); 24 CachedShader(VAddr addr, Maxwell::ShaderProgram program_type);
24 25
25 /// Gets the address of the shader in guest memory, required for cache management 26 VAddr GetAddr() const override {
26 VAddr GetAddr() const {
27 return addr; 27 return addr;
28 } 28 }
29 29
30 /// Gets the size of the shader in guest memory, required for cache management 30 std::size_t GetSizeInBytes() const override {
31 std::size_t GetSizeInBytes() const {
32 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); 31 return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);
33 } 32 }
34 33
34 // We do not have to flush this cache as things in it are never modified by us.
35 void Flush() override {}
36
35 /// Gets the shader entries for the shader 37 /// Gets the shader entries for the shader
36 const GLShader::ShaderEntries& GetShaderEntries() const { 38 const GLShader::ShaderEntries& GetShaderEntries() const {
37 return entries; 39 return entries;
38 } 40 }
39 41
40 /// Gets the GL program handle for the shader 42 /// Gets the GL program handle for the shader
41 GLuint GetProgramHandle() const { 43 GLuint GetProgramHandle(GLenum primitive_mode) {
42 return program.handle; 44 if (program_type != Maxwell::ShaderProgram::Geometry) {
45 return program.handle;
46 }
47 switch (primitive_mode) {
48 case GL_POINTS:
49 return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
50 case GL_LINES:
51 case GL_LINE_STRIP:
52 return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
53 case GL_LINES_ADJACENCY:
54 case GL_LINE_STRIP_ADJACENCY:
55 return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
56 "ShaderLinesAdjacency");
57 case GL_TRIANGLES:
58 case GL_TRIANGLE_STRIP:
59 case GL_TRIANGLE_FAN:
60 return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
61 case GL_TRIANGLES_ADJACENCY:
62 case GL_TRIANGLE_STRIP_ADJACENCY:
63 return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
64 "ShaderLines");
65 default:
66 UNREACHABLE_MSG("Unknown primitive mode.");
67 }
43 } 68 }
44 69
45 /// Gets the GL program resource location for the specified resource, caching as needed 70 /// Gets the GL program resource location for the specified resource, caching as needed
@@ -49,12 +74,30 @@ public:
49 GLint GetUniformLocation(const GLShader::SamplerEntry& sampler); 74 GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
50 75
51private: 76private:
77 /// Generates a geometry shader or returns one that already exists.
78 GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
79 const std::string& debug_name);
80
52 VAddr addr; 81 VAddr addr;
53 Maxwell::ShaderProgram program_type; 82 Maxwell::ShaderProgram program_type;
54 GLShader::ShaderSetup setup; 83 GLShader::ShaderSetup setup;
55 GLShader::ShaderEntries entries; 84 GLShader::ShaderEntries entries;
85
86 // Non-geometry program.
56 OGLProgram program; 87 OGLProgram program;
57 88
89 // Geometry programs. These are needed because GLSL needs an input topology but it's not
90 // declared by the hardware. Workaround this issue by generating a different shader per input
91 // topology class.
92 struct {
93 std::string code;
94 OGLProgram points;
95 OGLProgram lines;
96 OGLProgram lines_adjacency;
97 OGLProgram triangles;
98 OGLProgram triangles_adjacency;
99 } geometry_programs;
100
58 std::map<u32, GLuint> resource_cache; 101 std::map<u32, GLuint> resource_cache;
59 std::map<u32, GLint> uniform_cache; 102 std::map<u32, GLint> uniform_cache;
60}; 103};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 579a78702..09b003c59 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -3,9 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map> 5#include <map>
6#include <optional>
6#include <set> 7#include <set>
7#include <string> 8#include <string>
8#include <string_view> 9#include <string_view>
10#include <unordered_set>
9 11
10#include <fmt/format.h> 12#include <fmt/format.h>
11 13
@@ -29,11 +31,30 @@ using Tegra::Shader::SubOp;
29constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 31constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
30constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); 32constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
31 33
34constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
35constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
36
32class DecompileFail : public std::runtime_error { 37class DecompileFail : public std::runtime_error {
33public: 38public:
34 using std::runtime_error::runtime_error; 39 using std::runtime_error::runtime_error;
35}; 40};
36 41
42/// Translate topology
43static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
44 switch (topology) {
45 case Tegra::Shader::OutputTopology::PointList:
46 return "points";
47 case Tegra::Shader::OutputTopology::LineStrip:
48 return "line_strip";
49 case Tegra::Shader::OutputTopology::TriangleStrip:
50 return "triangle_strip";
51 default:
52 LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology));
53 UNREACHABLE();
54 return "points";
55 }
56}
57
37/// Describes the behaviour of code path of a given entry point and a return point. 58/// Describes the behaviour of code path of a given entry point and a return point.
38enum class ExitMethod { 59enum class ExitMethod {
39 Undetermined, ///< Internal value. Only occur when analyzing JMP loop. 60 Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
@@ -123,7 +144,7 @@ private:
123 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { 144 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
124 const Instruction instr = {program_code[offset]}; 145 const Instruction instr = {program_code[offset]};
125 if (const auto opcode = OpCode::Decode(instr)) { 146 if (const auto opcode = OpCode::Decode(instr)) {
126 switch (opcode->GetId()) { 147 switch (opcode->get().GetId()) {
127 case OpCode::Id::EXIT: { 148 case OpCode::Id::EXIT: {
128 // The EXIT instruction can be predicated, which means that the shader can 149 // The EXIT instruction can be predicated, which means that the shader can
129 // conditionally end on this instruction. We have to consider the case where the 150 // conditionally end on this instruction. We have to consider the case where the
@@ -143,10 +164,11 @@ private:
143 const ExitMethod jmp = Scan(target, end, labels); 164 const ExitMethod jmp = Scan(target, end, labels);
144 return exit_method = ParallelExit(no_jmp, jmp); 165 return exit_method = ParallelExit(no_jmp, jmp);
145 } 166 }
146 case OpCode::Id::SSY: { 167 case OpCode::Id::SSY:
147 // The SSY instruction uses a similar encoding as the BRA instruction. 168 case OpCode::Id::PBK: {
169 // The SSY and PBK use a similar encoding as the BRA instruction.
148 ASSERT_MSG(instr.bra.constant_buffer == 0, 170 ASSERT_MSG(instr.bra.constant_buffer == 0,
149 "Constant buffer SSY is not supported"); 171 "Constant buffer branching is not supported");
150 const u32 target = offset + instr.bra.GetBranchTarget(); 172 const u32 target = offset + instr.bra.GetBranchTarget();
151 labels.insert(target); 173 labels.insert(target);
152 // Continue scanning for an exit method. 174 // Continue scanning for an exit method.
@@ -253,8 +275,10 @@ enum class InternalFlag : u64 {
253class GLSLRegisterManager { 275class GLSLRegisterManager {
254public: 276public:
255 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, 277 GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
256 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix) 278 const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
257 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} { 279 const Tegra::Shader::Header& header)
280 : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header},
281 fixed_pipeline_output_attributes_used{}, local_memory_size{0} {
258 BuildRegisterList(); 282 BuildRegisterList();
259 BuildInputList(); 283 BuildInputList();
260 } 284 }
@@ -317,10 +341,10 @@ public:
317 */ 341 */
318 void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, 342 void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
319 u64 dest_num_components, u64 value_num_components, 343 u64 dest_num_components, u64 value_num_components,
320 bool is_saturated = false, u64 dest_elem = 0) { 344 bool is_saturated = false, u64 dest_elem = 0, bool precise = false) {
321 345
322 SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value, 346 SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
323 dest_num_components, value_num_components, dest_elem); 347 dest_num_components, value_num_components, dest_elem, precise);
324 } 348 }
325 349
326 /** 350 /**
@@ -344,28 +368,94 @@ public:
344 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; 368 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
345 369
346 SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', 370 SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
347 dest_num_components, value_num_components, dest_elem); 371 dest_num_components, value_num_components, dest_elem, false);
348 372
349 if (sets_cc) { 373 if (sets_cc) {
350 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; 374 const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
351 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); 375 SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
376 LOG_WARNING(HW_GPU, "Control Codes Imcomplete.");
352 } 377 }
353 } 378 }
354 379
355 /** 380 /**
381 * Writes code that does a register assignment to a half float value operation.
382 * @param reg The destination register to use.
383 * @param elem The element to use for the operation.
384 * @param value The code representing the value to assign. Type has to be half float.
385 * @param merge Half float kind of assignment.
386 * @param dest_num_components Number of components in the destination.
387 * @param value_num_components Number of components in the value.
388 * @param is_saturated Optional, when True, saturates the provided value.
389 * @param dest_elem Optional, the destination element to use for the operation.
390 */
391 void SetRegisterToHalfFloat(const Register& reg, u64 elem, const std::string& value,
392 Tegra::Shader::HalfMerge merge, u64 dest_num_components,
393 u64 value_num_components, bool is_saturated = false,
394 u64 dest_elem = 0) {
395 ASSERT_MSG(!is_saturated, "Unimplemented");
396
397 const std::string result = [&]() {
398 switch (merge) {
399 case Tegra::Shader::HalfMerge::H0_H1:
400 return "uintBitsToFloat(packHalf2x16(" + value + "))";
401 case Tegra::Shader::HalfMerge::F32:
402 // Half float instructions take the first component when doing a float cast.
403 return "float(" + value + ".x)";
404 case Tegra::Shader::HalfMerge::Mrg_H0:
405 // TODO(Rodrigo): I guess Mrg_H0 and Mrg_H1 take their respective component from the
406 // pack. I couldn't test this on hardware but it shouldn't really matter since most
407 // of the time when a Mrg_* flag is used both components will be mirrored. That
408 // being said, it deserves a test.
409 return "((" + GetRegisterAsInteger(reg, 0, false) +
410 " & 0xffff0000) | (packHalf2x16(" + value + ") & 0x0000ffff))";
411 case Tegra::Shader::HalfMerge::Mrg_H1:
412 return "((" + GetRegisterAsInteger(reg, 0, false) +
413 " & 0x0000ffff) | (packHalf2x16(" + value + ") & 0xffff0000))";
414 default:
415 UNREACHABLE();
416 return std::string("0");
417 }
418 }();
419
420 SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem, false);
421 }
422
423 /**
356 * Writes code that does a register assignment to input attribute operation. Input attributes 424 * Writes code that does a register assignment to input attribute operation. Input attributes
357 * are stored as floats, so this may require conversion. 425 * are stored as floats, so this may require conversion.
358 * @param reg The destination register to use. 426 * @param reg The destination register to use.
359 * @param elem The element to use for the operation. 427 * @param elem The element to use for the operation.
360 * @param attribute The input attribute to use as the source value. 428 * @param attribute The input attribute to use as the source value.
429 * @param input_mode The input mode.
430 * @param vertex The register that decides which vertex to read from (used in GS).
361 */ 431 */
362 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, 432 void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
363 const Tegra::Shader::IpaMode& input_mode) { 433 const Tegra::Shader::IpaMode& input_mode,
434 std::optional<Register> vertex = {}) {
364 const std::string dest = GetRegisterAsFloat(reg); 435 const std::string dest = GetRegisterAsFloat(reg);
365 const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem); 436 const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem);
366 shader.AddLine(dest + " = " + src + ';'); 437 shader.AddLine(dest + " = " + src + ';');
367 } 438 }
368 439
440 std::string GetLocalMemoryAsFloat(const std::string& index) {
441 return "lmem[" + index + ']';
442 }
443
444 std::string GetLocalMemoryAsInteger(const std::string& index, bool is_signed = false) {
445 const std::string func{is_signed ? "floatToIntBits" : "floatBitsToUint"};
446 return func + "(lmem[" + index + "])";
447 }
448
449 void SetLocalMemoryAsFloat(const std::string& index, const std::string& value) {
450 shader.AddLine("lmem[" + index + "] = " + value + ';');
451 }
452
453 void SetLocalMemoryAsInteger(const std::string& index, const std::string& value,
454 bool is_signed = false) {
455 const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"};
456 shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");");
457 }
458
369 std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { 459 std::string GetControlCode(const Tegra::Shader::ControlCode cc) const {
370 switch (cc) { 460 switch (cc) {
371 case Tegra::Shader::ControlCode::NEU: 461 case Tegra::Shader::ControlCode::NEU:
@@ -391,16 +481,34 @@ public:
391 * are stored as floats, so this may require conversion. 481 * are stored as floats, so this may require conversion.
392 * @param attribute The destination output attribute. 482 * @param attribute The destination output attribute.
393 * @param elem The element to use for the operation. 483 * @param elem The element to use for the operation.
394 * @param reg The register to use as the source value. 484 * @param val_reg The register to use as the source value.
485 * @param buf_reg The register that tells which buffer to write to (used in geometry shaders).
395 */ 486 */
396 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) { 487 void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& val_reg,
488 const Register& buf_reg) {
397 const std::string dest = GetOutputAttribute(attribute); 489 const std::string dest = GetOutputAttribute(attribute);
398 const std::string src = GetRegisterAsFloat(reg); 490 const std::string src = GetRegisterAsFloat(val_reg);
399 491
400 if (!dest.empty()) { 492 if (!dest.empty()) {
401 // Can happen with unknown/unimplemented output attributes, in which case we ignore the 493 // Can happen with unknown/unimplemented output attributes, in which case we ignore the
402 // instruction for now. 494 // instruction for now.
403 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); 495 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
496 // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
497 // shader. These instructions use a dirty register as buffer index. To avoid some
498 // drivers from complaining for the out of boundary writes, guard them.
499 const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " +
500 std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'};
501 shader.AddLine("amem[" + buf_index + "][" +
502 std::to_string(static_cast<u32>(attribute)) + ']' +
503 GetSwizzle(elem) + " = " + src + ';');
504 } else {
505 if (attribute == Attribute::Index::PointSize) {
506 fixed_pipeline_output_attributes_used.insert(attribute);
507 shader.AddLine(dest + " = " + src + ';');
508 } else {
509 shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
510 }
511 }
404 } 512 }
405 } 513 }
406 514
@@ -441,41 +549,127 @@ public:
441 } 549 }
442 } 550 }
443 551
444 /// Add declarations for registers 552 /// Add declarations.
445 void GenerateDeclarations(const std::string& suffix) { 553 void GenerateDeclarations(const std::string& suffix) {
554 GenerateVertex();
555 GenerateRegisters(suffix);
556 GenerateLocalMemory();
557 GenerateInternalFlags();
558 GenerateInputAttrs();
559 GenerateOutputAttrs();
560 GenerateConstBuffers();
561 GenerateSamplers();
562 GenerateGeometry();
563 }
564
565 /// Returns a list of constant buffer declarations.
566 std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
567 std::vector<ConstBufferEntry> result;
568 std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
569 std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
570 return result;
571 }
572
573 /// Returns a list of samplers used in the shader.
574 const std::vector<SamplerEntry>& GetSamplers() const {
575 return used_samplers;
576 }
577
578 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
579 /// necessary.
580 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
581 bool is_array, bool is_shadow) {
582 const auto offset = static_cast<std::size_t>(sampler.index.Value());
583
584 // If this sampler has already been used, return the existing mapping.
585 const auto itr =
586 std::find_if(used_samplers.begin(), used_samplers.end(),
587 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
588
589 if (itr != used_samplers.end()) {
590 ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
591 itr->IsShadow() == is_shadow);
592 return itr->GetName();
593 }
594
595 // Otherwise create a new mapping for this sampler
596 const std::size_t next_index = used_samplers.size();
597 const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow};
598 used_samplers.emplace_back(entry);
599 return entry.GetName();
600 }
601
602 void SetLocalMemory(u64 lmem) {
603 local_memory_size = lmem;
604 }
605
606private:
607 /// Generates declarations for registers.
608 void GenerateRegisters(const std::string& suffix) {
446 for (const auto& reg : regs) { 609 for (const auto& reg : regs) {
447 declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() + 610 declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() +
448 std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;"); 611 std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;");
449 } 612 }
450 declarations.AddNewLine(); 613 declarations.AddNewLine();
614 }
615
616 /// Generates declarations for local memory.
617 void GenerateLocalMemory() {
618 if (local_memory_size > 0) {
619 declarations.AddLine("float lmem[" + std::to_string((local_memory_size - 1 + 4) / 4) +
620 "];");
621 declarations.AddNewLine();
622 }
623 }
451 624
625 /// Generates declarations for internal flags.
626 void GenerateInternalFlags() {
452 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { 627 for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
453 const InternalFlag code = static_cast<InternalFlag>(ii); 628 const InternalFlag code = static_cast<InternalFlag>(ii);
454 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); 629 declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
455 } 630 }
456 declarations.AddNewLine(); 631 declarations.AddNewLine();
632 }
457 633
634 /// Generates declarations for input attributes.
635 void GenerateInputAttrs() {
458 for (const auto element : declr_input_attribute) { 636 for (const auto element : declr_input_attribute) {
459 // TODO(bunnei): Use proper number of elements for these 637 // TODO(bunnei): Use proper number of elements for these
460 u32 idx = 638 u32 idx =
461 static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0); 639 static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0);
462 declarations.AddLine("layout(location = " + std::to_string(idx) + ")" + 640 if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
463 GetInputFlags(element.first) + "in vec4 " + 641 // If inputs are varyings, add an offset
464 GetInputAttribute(element.first, element.second) + ';'); 642 idx += GENERIC_VARYING_START_LOCATION;
643 }
644
645 std::string attr{GetInputAttribute(element.first, element.second)};
646 if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
647 attr = "gs_" + attr + "[]";
648 }
649 declarations.AddLine("layout (location = " + std::to_string(idx) + ") " +
650 GetInputFlags(element.first) + "in vec4 " + attr + ';');
465 } 651 }
652
466 declarations.AddNewLine(); 653 declarations.AddNewLine();
654 }
467 655
656 /// Generates declarations for output attributes.
657 void GenerateOutputAttrs() {
468 for (const auto& index : declr_output_attribute) { 658 for (const auto& index : declr_output_attribute) {
469 // TODO(bunnei): Use proper number of elements for these 659 // TODO(bunnei): Use proper number of elements for these
470 declarations.AddLine("layout(location = " + 660 const u32 idx = static_cast<u32>(index) -
471 std::to_string(static_cast<u32>(index) - 661 static_cast<u32>(Attribute::Index::Attribute_0) +
472 static_cast<u32>(Attribute::Index::Attribute_0)) + 662 GENERIC_VARYING_START_LOCATION;
473 ") out vec4 " + GetOutputAttribute(index) + ';'); 663 declarations.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
664 GetOutputAttribute(index) + ';');
474 } 665 }
475 declarations.AddNewLine(); 666 declarations.AddNewLine();
667 }
476 668
669 /// Generates declarations for constant buffers.
670 void GenerateConstBuffers() {
477 for (const auto& entry : GetConstBuffersDeclarations()) { 671 for (const auto& entry : GetConstBuffersDeclarations()) {
478 declarations.AddLine("layout(std140) uniform " + entry.GetName()); 672 declarations.AddLine("layout (std140) uniform " + entry.GetName());
479 declarations.AddLine('{'); 673 declarations.AddLine('{');
480 declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) + 674 declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
481 "[MAX_CONSTBUFFER_ELEMENTS];"); 675 "[MAX_CONSTBUFFER_ELEMENTS];");
@@ -483,7 +677,10 @@ public:
483 declarations.AddNewLine(); 677 declarations.AddNewLine();
484 } 678 }
485 declarations.AddNewLine(); 679 declarations.AddNewLine();
680 }
486 681
682 /// Generates declarations for samplers.
683 void GenerateSamplers() {
487 const auto& samplers = GetSamplers(); 684 const auto& samplers = GetSamplers();
488 for (const auto& sampler : samplers) { 685 for (const auto& sampler : samplers) {
489 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() + 686 declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
@@ -492,43 +689,56 @@ public:
492 declarations.AddNewLine(); 689 declarations.AddNewLine();
493 } 690 }
494 691
495 /// Returns a list of constant buffer declarations 692 /// Generates declarations used for geometry shaders.
496 std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const { 693 void GenerateGeometry() {
497 std::vector<ConstBufferEntry> result; 694 if (stage != Maxwell3D::Regs::ShaderStage::Geometry)
498 std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(), 695 return;
499 std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
500 return result;
501 }
502 696
503 /// Returns a list of samplers used in the shader 697 declarations.AddLine(
504 const std::vector<SamplerEntry>& GetSamplers() const { 698 "layout (" + GetTopologyName(header.common3.output_topology) +
505 return used_samplers; 699 ", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;");
506 } 700 declarations.AddNewLine();
507 701
508 /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if 702 declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" +
509 /// necessary. 703 std::to_string(MAX_ATTRIBUTES) + "];");
510 std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, 704 declarations.AddNewLine();
511 bool is_array) {
512 const std::size_t offset = static_cast<std::size_t>(sampler.index.Value());
513 705
514 // If this sampler has already been used, return the existing mapping. 706 constexpr char buffer[] = "amem[output_buffer]";
515 const auto itr = 707 declarations.AddLine("void emit_vertex(uint output_buffer) {");
516 std::find_if(used_samplers.begin(), used_samplers.end(), 708 ++declarations.scope;
517 [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); 709 for (const auto element : declr_output_attribute) {
710 declarations.AddLine(GetOutputAttribute(element) + " = " + buffer + '[' +
711 std::to_string(static_cast<u32>(element)) + "];");
712 }
713
714 declarations.AddLine("position = " + std::string(buffer) + '[' +
715 std::to_string(static_cast<u32>(Attribute::Index::Position)) + "];");
716
717 // If a geometry shader is attached, it will always flip (it's the last stage before
718 // fragment). For more info about flipping, refer to gl_shader_gen.cpp.
719 declarations.AddLine("position.xy *= viewport_flip.xy;");
720 declarations.AddLine("gl_Position = position;");
721 declarations.AddLine("position.w = 1.0;");
722 declarations.AddLine("EmitVertex();");
723 --declarations.scope;
724 declarations.AddLine('}');
725 declarations.AddNewLine();
726 }
518 727
519 if (itr != used_samplers.end()) { 728 void GenerateVertex() {
520 ASSERT(itr->GetType() == type && itr->IsArray() == is_array); 729 if (stage != Maxwell3D::Regs::ShaderStage::Vertex)
521 return itr->GetName(); 730 return;
731 declarations.AddLine("out gl_PerVertex {");
732 ++declarations.scope;
733 declarations.AddLine("vec4 gl_Position;");
734 for (auto& o : fixed_pipeline_output_attributes_used) {
735 if (o == Attribute::Index::PointSize)
736 declarations.AddLine("float gl_PointSize;");
522 } 737 }
523 738 --declarations.scope;
524 // Otherwise create a new mapping for this sampler 739 declarations.AddLine("};");
525 const std::size_t next_index = used_samplers.size();
526 const SamplerEntry entry{stage, offset, next_index, type, is_array};
527 used_samplers.emplace_back(entry);
528 return entry.GetName();
529 } 740 }
530 741
531private:
532 /// Generates code representing a temporary (GPR) register. 742 /// Generates code representing a temporary (GPR) register.
533 std::string GetRegister(const Register& reg, unsigned elem) { 743 std::string GetRegister(const Register& reg, unsigned elem) {
534 if (reg == Register::ZeroIndex) { 744 if (reg == Register::ZeroIndex) {
@@ -548,7 +758,8 @@ private:
548 * @param dest_elem Optional, the destination element to use for the operation. 758 * @param dest_elem Optional, the destination element to use for the operation.
549 */ 759 */
550 void SetRegister(const Register& reg, u64 elem, const std::string& value, 760 void SetRegister(const Register& reg, u64 elem, const std::string& value,
551 u64 dest_num_components, u64 value_num_components, u64 dest_elem) { 761 u64 dest_num_components, u64 value_num_components, u64 dest_elem,
762 bool precise) {
552 if (reg == Register::ZeroIndex) { 763 if (reg == Register::ZeroIndex) {
553 LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); 764 LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex");
554 UNREACHABLE(); 765 UNREACHABLE();
@@ -565,7 +776,18 @@ private:
565 src += GetSwizzle(elem); 776 src += GetSwizzle(elem);
566 } 777 }
567 778
568 shader.AddLine(dest + " = " + src + ';'); 779 if (precise && stage != Maxwell3D::Regs::ShaderStage::Fragment) {
780 shader.AddLine('{');
781 ++shader.scope;
782 // This avoids optimizations of constant propagation and keeps the code as the original
783 // Sadly using the precise keyword causes "linking" errors on fragment shaders.
784 shader.AddLine("precise float tmp = " + src + ';');
785 shader.AddLine(dest + " = tmp;");
786 --shader.scope;
787 shader.AddLine('}');
788 } else {
789 shader.AddLine(dest + " = " + src + ';');
790 }
569 } 791 }
570 792
571 /// Build the GLSL register list. 793 /// Build the GLSL register list.
@@ -585,11 +807,19 @@ private:
585 807
586 /// Generates code representing an input attribute register. 808 /// Generates code representing an input attribute register.
587 std::string GetInputAttribute(Attribute::Index attribute, 809 std::string GetInputAttribute(Attribute::Index attribute,
588 const Tegra::Shader::IpaMode& input_mode) { 810 const Tegra::Shader::IpaMode& input_mode,
811 std::optional<Register> vertex = {}) {
812 auto GeometryPass = [&](const std::string& name) {
813 if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
814 return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) + ']';
815 }
816 return name;
817 };
818
589 switch (attribute) { 819 switch (attribute) {
590 case Attribute::Index::Position: 820 case Attribute::Index::Position:
591 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) { 821 if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
592 return "position"; 822 return GeometryPass("position");
593 } else { 823 } else {
594 return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)"; 824 return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)";
595 } 825 }
@@ -618,7 +848,7 @@ private:
618 UNREACHABLE(); 848 UNREACHABLE();
619 } 849 }
620 } 850 }
621 return "input_attribute_" + std::to_string(index); 851 return GeometryPass("input_attribute_" + std::to_string(index));
622 } 852 }
623 853
624 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute)); 854 LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute));
@@ -671,9 +901,11 @@ private:
671 return out; 901 return out;
672 } 902 }
673 903
674 /// Generates code representing an output attribute register. 904 /// Generates code representing the declaration name of an output attribute register.
675 std::string GetOutputAttribute(Attribute::Index attribute) { 905 std::string GetOutputAttribute(Attribute::Index attribute) {
676 switch (attribute) { 906 switch (attribute) {
907 case Attribute::Index::PointSize:
908 return "gl_PointSize";
677 case Attribute::Index::Position: 909 case Attribute::Index::Position:
678 return "position"; 910 return "position";
679 default: 911 default:
@@ -707,6 +939,9 @@ private:
707 std::vector<SamplerEntry> used_samplers; 939 std::vector<SamplerEntry> used_samplers;
708 const Maxwell3D::Regs::ShaderStage& stage; 940 const Maxwell3D::Regs::ShaderStage& stage;
709 const std::string& suffix; 941 const std::string& suffix;
942 const Tegra::Shader::Header& header;
943 std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used;
944 u64 local_memory_size;
710}; 945};
711 946
712class GLSLGenerator { 947class GLSLGenerator {
@@ -716,6 +951,8 @@ public:
716 : subroutines(subroutines), program_code(program_code), main_offset(main_offset), 951 : subroutines(subroutines), program_code(program_code), main_offset(main_offset),
717 stage(stage), suffix(suffix) { 952 stage(stage), suffix(suffix) {
718 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); 953 std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header));
954 local_memory_size = header.GetLocalMemorySize();
955 regs.SetLocalMemory(local_memory_size);
719 Generate(suffix); 956 Generate(suffix);
720 } 957 }
721 958
@@ -746,9 +983,23 @@ private:
746 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32()); 983 return fmt::format("uintBitsToFloat({})", instr.alu.GetImm20_32());
747 } 984 }
748 985
986 /// Generates code representing a vec2 pair unpacked from a half float immediate
987 static std::string UnpackHalfImmediate(const Instruction& instr, bool negate) {
988 const std::string immediate = GetHalfFloat(std::to_string(instr.half_imm.PackImmediates()));
989 if (!negate) {
990 return immediate;
991 }
992 const std::string negate_first = instr.half_imm.first_negate != 0 ? "-" : "";
993 const std::string negate_second = instr.half_imm.second_negate != 0 ? "-" : "";
994 const std::string negate_vec = "vec2(" + negate_first + "1, " + negate_second + "1)";
995
996 return '(' + immediate + " * " + negate_vec + ')';
997 }
998
749 /// Generates code representing a texture sampler. 999 /// Generates code representing a texture sampler.
750 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) { 1000 std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array,
751 return regs.AccessSampler(sampler, type, is_array); 1001 bool is_shadow) {
1002 return regs.AccessSampler(sampler, type, is_array, is_shadow);
752 } 1003 }
753 1004
754 /** 1005 /**
@@ -776,7 +1027,7 @@ private:
776 // Can't assign to the constant predicate. 1027 // Can't assign to the constant predicate.
777 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); 1028 ASSERT(pred != static_cast<u64>(Pred::UnusedIndex));
778 1029
779 const std::string variable = 'p' + std::to_string(pred) + '_' + suffix; 1030 std::string variable = 'p' + std::to_string(pred) + '_' + suffix;
780 shader.AddLine(variable + " = " + value + ';'); 1031 shader.AddLine(variable + " = " + value + ';');
781 declr_predicates.insert(std::move(variable)); 1032 declr_predicates.insert(std::move(variable));
782 } 1033 }
@@ -881,6 +1132,41 @@ private:
881 } 1132 }
882 1133
883 /* 1134 /*
1135 * Transforms the input string GLSL operand into an unpacked half float pair.
1136 * @note This function returns a float type pair instead of a half float pair. This is because
1137 * real half floats are not standardized in GLSL but unpackHalf2x16 (which returns a vec2) is.
1138 * @param operand Input operand. It has to be an unsigned integer.
1139 * @param type How to unpack the unsigned integer to a half float pair.
1140 * @param abs Get the absolute value of unpacked half floats.
1141 * @param neg Get the negative value of unpacked half floats.
1142 * @returns String corresponding to a half float pair.
1143 */
1144 static std::string GetHalfFloat(const std::string& operand,
1145 Tegra::Shader::HalfType type = Tegra::Shader::HalfType::H0_H1,
1146 bool abs = false, bool neg = false) {
1147 // "vec2" calls emitted in this function are intended to alias components.
1148 const std::string value = [&]() {
1149 switch (type) {
1150 case Tegra::Shader::HalfType::H0_H1:
1151 return "unpackHalf2x16(" + operand + ')';
1152 case Tegra::Shader::HalfType::F32:
1153 return "vec2(uintBitsToFloat(" + operand + "))";
1154 case Tegra::Shader::HalfType::H0_H0:
1155 case Tegra::Shader::HalfType::H1_H1: {
1156 const bool high = type == Tegra::Shader::HalfType::H1_H1;
1157 const char unpack_index = "xy"[high ? 1 : 0];
1158 return "vec2(unpackHalf2x16(" + operand + ")." + unpack_index + ')';
1159 }
1160 default:
1161 UNREACHABLE();
1162 return std::string("vec2(0)");
1163 }
1164 }();
1165
1166 return GetOperandAbsNeg(value, abs, neg);
1167 }
1168
1169 /*
884 * Returns whether the instruction at the specified offset is a 'sched' instruction. 1170 * Returns whether the instruction at the specified offset is a 'sched' instruction.
885 * Sched instructions always appear before a sequence of 3 instructions. 1171 * Sched instructions always appear before a sequence of 3 instructions.
886 */ 1172 */
@@ -1002,28 +1288,47 @@ private:
1002 shader.AddLine('}'); 1288 shader.AddLine('}');
1003 } 1289 }
1004 1290
1291 static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) {
1292 switch (texture_type) {
1293 case Tegra::Shader::TextureType::Texture1D: {
1294 return 1;
1295 }
1296 case Tegra::Shader::TextureType::Texture2D: {
1297 return 2;
1298 }
1299 case Tegra::Shader::TextureType::Texture3D:
1300 case Tegra::Shader::TextureType::TextureCube: {
1301 return 3;
1302 }
1303 default:
1304 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast<u32>(texture_type));
1305 UNREACHABLE();
1306 return 0;
1307 }
1308 }
1309
1005 /* 1310 /*
1006 * Emits code to push the input target address to the SSY address stack, incrementing the stack 1311 * Emits code to push the input target address to the flow address stack, incrementing the stack
1007 * top. 1312 * top.
1008 */ 1313 */
1009 void EmitPushToSSYStack(u32 target) { 1314 void EmitPushToFlowStack(u32 target) {
1010 shader.AddLine('{'); 1315 shader.AddLine('{');
1011 ++shader.scope; 1316 ++shader.scope;
1012 shader.AddLine("ssy_stack[ssy_stack_top] = " + std::to_string(target) + "u;"); 1317 shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;");
1013 shader.AddLine("ssy_stack_top++;"); 1318 shader.AddLine("flow_stack_top++;");
1014 --shader.scope; 1319 --shader.scope;
1015 shader.AddLine('}'); 1320 shader.AddLine('}');
1016 } 1321 }
1017 1322
1018 /* 1323 /*
1019 * Emits code to pop an address from the SSY address stack, setting the jump address to the 1324 * Emits code to pop an address from the flow address stack, setting the jump address to the
1020 * popped address and decrementing the stack top. 1325 * popped address and decrementing the stack top.
1021 */ 1326 */
1022 void EmitPopFromSSYStack() { 1327 void EmitPopFromFlowStack() {
1023 shader.AddLine('{'); 1328 shader.AddLine('{');
1024 ++shader.scope; 1329 ++shader.scope;
1025 shader.AddLine("ssy_stack_top--;"); 1330 shader.AddLine("flow_stack_top--;");
1026 shader.AddLine("jmp_to = ssy_stack[ssy_stack_top];"); 1331 shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
1027 shader.AddLine("break;"); 1332 shader.AddLine("break;");
1028 --shader.scope; 1333 --shader.scope;
1029 shader.AddLine('}'); 1334 shader.AddLine('}');
@@ -1035,9 +1340,29 @@ private:
1035 1340
1036 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); 1341 ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented");
1037 1342
1343 shader.AddLine("if (alpha_test[0] != 0) {");
1344 ++shader.scope;
1345 // We start on the register containing the alpha value in the first RT.
1346 u32 current_reg = 3;
1347 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1348 ++render_target) {
1349 // TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
1350 // multiple render targets are used.
1351 if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
1352 header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
1353 header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
1354 header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
1355 shader.AddLine(fmt::format("if (!AlphaFunc({})) discard;",
1356 regs.GetRegisterAsFloat(current_reg)));
1357 current_reg += 4;
1358 }
1359 }
1360 --shader.scope;
1361 shader.AddLine('}');
1362
1038 // Write the color outputs using the data in the shader registers, disabled 1363 // Write the color outputs using the data in the shader registers, disabled
1039 // rendertargets/components are skipped in the register assignment. 1364 // rendertargets/components are skipped in the register assignment.
1040 u32 current_reg = 0; 1365 current_reg = 0;
1041 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets; 1366 for (u32 render_target = 0; render_target < Maxwell3D::Regs::NumRenderTargets;
1042 ++render_target) { 1367 ++render_target) {
1043 // TODO(Subv): Figure out how dual-source blending is configured in the Switch. 1368 // TODO(Subv): Figure out how dual-source blending is configured in the Switch.
@@ -1061,6 +1386,63 @@ private:
1061 } 1386 }
1062 } 1387 }
1063 1388
1389 /// Unpacks a video instruction operand (e.g. VMAD).
1390 std::string GetVideoOperand(const std::string& op, bool is_chunk, bool is_signed,
1391 Tegra::Shader::VideoType type, u64 byte_height) {
1392 const std::string value = [&]() {
1393 if (!is_chunk) {
1394 const auto offset = static_cast<u32>(byte_height * 8);
1395 return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)";
1396 }
1397 const std::string zero = "0";
1398
1399 switch (type) {
1400 case Tegra::Shader::VideoType::Size16_Low:
1401 return '(' + op + " & 0xffff)";
1402 case Tegra::Shader::VideoType::Size16_High:
1403 return '(' + op + " >> 16)";
1404 case Tegra::Shader::VideoType::Size32:
1405 // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when
1406 // this type is used (1 * 1 + 0 == 0x5b800000). Until a better
1407 // explanation is found: assert.
1408 UNIMPLEMENTED();
1409 return zero;
1410 case Tegra::Shader::VideoType::Invalid:
1411 UNREACHABLE_MSG("Invalid instruction encoding");
1412 return zero;
1413 default:
1414 UNREACHABLE();
1415 return zero;
1416 }
1417 }();
1418
1419 if (is_signed) {
1420 return "int(" + value + ')';
1421 }
1422 return value;
1423 };
1424
1425 /// Gets the A operand for a video instruction.
1426 std::string GetVideoOperandA(Instruction instr) {
1427 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr8, 0, false),
1428 instr.video.is_byte_chunk_a != 0, instr.video.signed_a,
1429 instr.video.type_a, instr.video.byte_height_a);
1430 }
1431
1432 /// Gets the B operand for a video instruction.
1433 std::string GetVideoOperandB(Instruction instr) {
1434 if (instr.video.use_register_b) {
1435 return GetVideoOperand(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
1436 instr.video.is_byte_chunk_b != 0, instr.video.signed_b,
1437 instr.video.type_b, instr.video.byte_height_b);
1438 } else {
1439 return '(' +
1440 std::to_string(instr.video.signed_b ? static_cast<s16>(instr.alu.GetImm20_16())
1441 : instr.alu.GetImm20_16()) +
1442 ')';
1443 }
1444 }
1445
1064 /** 1446 /**
1065 * Compiles a single instruction from Tegra to GLSL. 1447 * Compiles a single instruction from Tegra to GLSL.
1066 * @param offset the offset of the Tegra shader instruction. 1448 * @param offset the offset of the Tegra shader instruction.
@@ -1083,8 +1465,8 @@ private:
1083 return offset + 1; 1465 return offset + 1;
1084 } 1466 }
1085 1467
1086 shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" + 1468 shader.AddLine(
1087 std::to_string(instr.value) + ')'); 1469 fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value));
1088 1470
1089 using Tegra::Shader::Pred; 1471 using Tegra::Shader::Pred;
1090 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 1472 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -1092,7 +1474,7 @@ private:
1092 1474
1093 // Some instructions (like SSY) don't have a predicate field, they are always 1475 // Some instructions (like SSY) don't have a predicate field, they are always
1094 // unconditionally executed. 1476 // unconditionally executed.
1095 bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->GetId()); 1477 bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->get().GetId());
1096 1478
1097 if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { 1479 if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
1098 shader.AddLine("if (" + 1480 shader.AddLine("if (" +
@@ -1102,7 +1484,7 @@ private:
1102 ++shader.scope; 1484 ++shader.scope;
1103 } 1485 }
1104 1486
1105 switch (opcode->GetType()) { 1487 switch (opcode->get().GetType()) {
1106 case OpCode::Type::Arithmetic: { 1488 case OpCode::Type::Arithmetic: {
1107 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 1489 std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1108 1490
@@ -1119,7 +1501,7 @@ private:
1119 } 1501 }
1120 } 1502 }
1121 1503
1122 switch (opcode->GetId()) { 1504 switch (opcode->get().GetId()) {
1123 case OpCode::Id::MOV_C: 1505 case OpCode::Id::MOV_C:
1124 case OpCode::Id::MOV_R: { 1506 case OpCode::Id::MOV_R: {
1125 // MOV does not have neither 'abs' nor 'neg' bits. 1507 // MOV does not have neither 'abs' nor 'neg' bits.
@@ -1141,8 +1523,13 @@ private:
1141 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); 1523 ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented");
1142 1524
1143 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); 1525 op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b);
1526
1144 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, 1527 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
1145 instr.alu.saturate_d); 1528 instr.alu.saturate_d, 0, true);
1529 if (instr.generates_cc) {
1530 LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code");
1531 UNREACHABLE();
1532 }
1146 break; 1533 break;
1147 } 1534 }
1148 case OpCode::Id::FADD_C: 1535 case OpCode::Id::FADD_C:
@@ -1150,8 +1537,13 @@ private:
1150 case OpCode::Id::FADD_IMM: { 1537 case OpCode::Id::FADD_IMM: {
1151 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); 1538 op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a);
1152 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); 1539 op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b);
1540
1153 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, 1541 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
1154 instr.alu.saturate_d); 1542 instr.alu.saturate_d, 0, true);
1543 if (instr.generates_cc) {
1544 LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code");
1545 UNREACHABLE();
1546 }
1155 break; 1547 break;
1156 } 1548 }
1157 case OpCode::Id::MUFU: { 1549 case OpCode::Id::MUFU: {
@@ -1159,31 +1551,31 @@ private:
1159 switch (instr.sub_op) { 1551 switch (instr.sub_op) {
1160 case SubOp::Cos: 1552 case SubOp::Cos:
1161 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, 1553 regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
1162 instr.alu.saturate_d); 1554 instr.alu.saturate_d, 0, true);
1163 break; 1555 break;
1164 case SubOp::Sin: 1556 case SubOp::Sin:
1165 regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1, 1557 regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
1166 instr.alu.saturate_d); 1558 instr.alu.saturate_d, 0, true);
1167 break; 1559 break;
1168 case SubOp::Ex2: 1560 case SubOp::Ex2:
1169 regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1, 1561 regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
1170 instr.alu.saturate_d); 1562 instr.alu.saturate_d, 0, true);
1171 break; 1563 break;
1172 case SubOp::Lg2: 1564 case SubOp::Lg2:
1173 regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1, 1565 regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
1174 instr.alu.saturate_d); 1566 instr.alu.saturate_d, 0, true);
1175 break; 1567 break;
1176 case SubOp::Rcp: 1568 case SubOp::Rcp:
1177 regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, 1569 regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
1178 instr.alu.saturate_d); 1570 instr.alu.saturate_d, 0, true);
1179 break; 1571 break;
1180 case SubOp::Rsq: 1572 case SubOp::Rsq:
1181 regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1, 1573 regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
1182 instr.alu.saturate_d); 1574 instr.alu.saturate_d, 0, true);
1183 break; 1575 break;
1184 case SubOp::Sqrt: 1576 case SubOp::Sqrt:
1185 regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1, 1577 regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1,
1186 instr.alu.saturate_d); 1578 instr.alu.saturate_d, 0, true);
1187 break; 1579 break;
1188 default: 1580 default:
1189 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", 1581 LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}",
@@ -1204,7 +1596,11 @@ private:
1204 regs.SetRegisterToFloat(instr.gpr0, 0, 1596 regs.SetRegisterToFloat(instr.gpr0, 0,
1205 '(' + condition + ") ? min(" + parameters + ") : max(" + 1597 '(' + condition + ") ? min(" + parameters + ") : max(" +
1206 parameters + ')', 1598 parameters + ')',
1207 1, 1); 1599 1, 1, false, 0, true);
1600 if (instr.generates_cc) {
1601 LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code");
1602 UNREACHABLE();
1603 }
1208 break; 1604 break;
1209 } 1605 }
1210 case OpCode::Id::RRO_C: 1606 case OpCode::Id::RRO_C:
@@ -1217,22 +1613,28 @@ private:
1217 break; 1613 break;
1218 } 1614 }
1219 default: { 1615 default: {
1220 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); 1616 LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}",
1617 opcode->get().GetName());
1221 UNREACHABLE(); 1618 UNREACHABLE();
1222 } 1619 }
1223 } 1620 }
1224 break; 1621 break;
1225 } 1622 }
1226 case OpCode::Type::ArithmeticImmediate: { 1623 case OpCode::Type::ArithmeticImmediate: {
1227 switch (opcode->GetId()) { 1624 switch (opcode->get().GetId()) {
1228 case OpCode::Id::MOV32_IMM: { 1625 case OpCode::Id::MOV32_IMM: {
1229 regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1); 1626 regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
1230 break; 1627 break;
1231 } 1628 }
1232 case OpCode::Id::FMUL32_IMM: { 1629 case OpCode::Id::FMUL32_IMM: {
1233 regs.SetRegisterToFloat( 1630 regs.SetRegisterToFloat(instr.gpr0, 0,
1234 instr.gpr0, 0, 1631 regs.GetRegisterAsFloat(instr.gpr8) + " * " +
1235 regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1); 1632 GetImmediate32(instr),
1633 1, 1, instr.fmul32.saturate, 0, true);
1634 if (instr.op_32.generates_cc) {
1635 LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code");
1636 UNREACHABLE();
1637 }
1236 break; 1638 break;
1237 } 1639 }
1238 case OpCode::Id::FADD32I: { 1640 case OpCode::Id::FADD32I: {
@@ -1255,7 +1657,11 @@ private:
1255 op_b = "-(" + op_b + ')'; 1657 op_b = "-(" + op_b + ')';
1256 } 1658 }
1257 1659
1258 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1); 1660 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true);
1661 if (instr.op_32.generates_cc) {
1662 LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code");
1663 UNREACHABLE();
1664 }
1259 break; 1665 break;
1260 } 1666 }
1261 } 1667 }
@@ -1267,7 +1673,7 @@ private:
1267 std::string op_a = instr.bfe.negate_a ? "-" : ""; 1673 std::string op_a = instr.bfe.negate_a ? "-" : "";
1268 op_a += regs.GetRegisterAsInteger(instr.gpr8); 1674 op_a += regs.GetRegisterAsInteger(instr.gpr8);
1269 1675
1270 switch (opcode->GetId()) { 1676 switch (opcode->get().GetId()) {
1271 case OpCode::Id::BFE_IMM: { 1677 case OpCode::Id::BFE_IMM: {
1272 std::string inner_shift = 1678 std::string inner_shift =
1273 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; 1679 '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')';
@@ -1276,17 +1682,20 @@ private:
1276 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; 1682 std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')';
1277 1683
1278 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); 1684 regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1);
1685 if (instr.generates_cc) {
1686 LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code");
1687 UNREACHABLE();
1688 }
1279 break; 1689 break;
1280 } 1690 }
1281 default: { 1691 default: {
1282 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName()); 1692 LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName());
1283 UNREACHABLE(); 1693 UNREACHABLE();
1284 } 1694 }
1285 } 1695 }
1286 1696
1287 break; 1697 break;
1288 } 1698 }
1289
1290 case OpCode::Type::Shift: { 1699 case OpCode::Type::Shift: {
1291 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1700 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1292 std::string op_b; 1701 std::string op_b;
@@ -1302,7 +1711,7 @@ private:
1302 } 1711 }
1303 } 1712 }
1304 1713
1305 switch (opcode->GetId()) { 1714 switch (opcode->get().GetId()) {
1306 case OpCode::Id::SHR_C: 1715 case OpCode::Id::SHR_C:
1307 case OpCode::Id::SHR_R: 1716 case OpCode::Id::SHR_R:
1308 case OpCode::Id::SHR_IMM: { 1717 case OpCode::Id::SHR_IMM: {
@@ -1314,32 +1723,43 @@ private:
1314 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift 1723 // Cast to int is superfluous for arithmetic shift, it's only for a logical shift
1315 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', 1724 regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
1316 1, 1); 1725 1, 1);
1726 if (instr.generates_cc) {
1727 LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code");
1728 UNREACHABLE();
1729 }
1317 break; 1730 break;
1318 } 1731 }
1319 case OpCode::Id::SHL_C: 1732 case OpCode::Id::SHL_C:
1320 case OpCode::Id::SHL_R: 1733 case OpCode::Id::SHL_R:
1321 case OpCode::Id::SHL_IMM: 1734 case OpCode::Id::SHL_IMM:
1322 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); 1735 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1);
1736 if (instr.generates_cc) {
1737 LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code");
1738 UNREACHABLE();
1739 }
1323 break; 1740 break;
1324 default: { 1741 default: {
1325 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); 1742 LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName());
1326 UNREACHABLE(); 1743 UNREACHABLE();
1327 } 1744 }
1328 } 1745 }
1329 break; 1746 break;
1330 } 1747 }
1331
1332 case OpCode::Type::ArithmeticIntegerImmediate: { 1748 case OpCode::Type::ArithmeticIntegerImmediate: {
1333 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1749 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1334 std::string op_b = std::to_string(instr.alu.imm20_32.Value()); 1750 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
1335 1751
1336 switch (opcode->GetId()) { 1752 switch (opcode->get().GetId()) {
1337 case OpCode::Id::IADD32I: 1753 case OpCode::Id::IADD32I:
1338 if (instr.iadd32i.negate_a) 1754 if (instr.iadd32i.negate_a)
1339 op_a = "-(" + op_a + ')'; 1755 op_a = "-(" + op_a + ')';
1340 1756
1341 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1757 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1342 instr.iadd32i.saturate != 0); 1758 instr.iadd32i.saturate != 0);
1759 if (instr.op_32.generates_cc) {
1760 LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code");
1761 UNREACHABLE();
1762 }
1343 break; 1763 break;
1344 case OpCode::Id::LOP32I: { 1764 case OpCode::Id::LOP32I: {
1345 if (instr.alu.lop32i.invert_a) 1765 if (instr.alu.lop32i.invert_a)
@@ -1351,11 +1771,15 @@ private:
1351 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, 1771 WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
1352 Tegra::Shader::PredicateResultMode::None, 1772 Tegra::Shader::PredicateResultMode::None,
1353 Tegra::Shader::Pred::UnusedIndex); 1773 Tegra::Shader::Pred::UnusedIndex);
1774 if (instr.op_32.generates_cc) {
1775 LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code");
1776 UNREACHABLE();
1777 }
1354 break; 1778 break;
1355 } 1779 }
1356 default: { 1780 default: {
1357 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", 1781 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}",
1358 opcode->GetName()); 1782 opcode->get().GetName());
1359 UNREACHABLE(); 1783 UNREACHABLE();
1360 } 1784 }
1361 } 1785 }
@@ -1375,7 +1799,7 @@ private:
1375 } 1799 }
1376 } 1800 }
1377 1801
1378 switch (opcode->GetId()) { 1802 switch (opcode->get().GetId()) {
1379 case OpCode::Id::IADD_C: 1803 case OpCode::Id::IADD_C:
1380 case OpCode::Id::IADD_R: 1804 case OpCode::Id::IADD_R:
1381 case OpCode::Id::IADD_IMM: { 1805 case OpCode::Id::IADD_IMM: {
@@ -1387,6 +1811,10 @@ private:
1387 1811
1388 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, 1812 regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
1389 instr.alu.saturate_d); 1813 instr.alu.saturate_d);
1814 if (instr.generates_cc) {
1815 LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code");
1816 UNREACHABLE();
1817 }
1390 break; 1818 break;
1391 } 1819 }
1392 case OpCode::Id::IADD3_C: 1820 case OpCode::Id::IADD3_C:
@@ -1411,7 +1839,7 @@ private:
1411 } 1839 }
1412 }; 1840 };
1413 1841
1414 if (opcode->GetId() == OpCode::Id::IADD3_R) { 1842 if (opcode->get().GetId() == OpCode::Id::IADD3_R) {
1415 apply_height(instr.iadd3.height_a, op_a); 1843 apply_height(instr.iadd3.height_a, op_a);
1416 apply_height(instr.iadd3.height_b, op_b); 1844 apply_height(instr.iadd3.height_b, op_b);
1417 apply_height(instr.iadd3.height_c, op_c); 1845 apply_height(instr.iadd3.height_c, op_c);
@@ -1427,7 +1855,7 @@ private:
1427 op_c = "-(" + op_c + ')'; 1855 op_c = "-(" + op_c + ')';
1428 1856
1429 std::string result; 1857 std::string result;
1430 if (opcode->GetId() == OpCode::Id::IADD3_R) { 1858 if (opcode->get().GetId() == OpCode::Id::IADD3_R) {
1431 switch (instr.iadd3.mode) { 1859 switch (instr.iadd3.mode) {
1432 case Tegra::Shader::IAdd3Mode::RightShift: 1860 case Tegra::Shader::IAdd3Mode::RightShift:
1433 // TODO(tech4me): According to 1861 // TODO(tech4me): According to
@@ -1448,6 +1876,11 @@ private:
1448 } 1876 }
1449 1877
1450 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); 1878 regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1);
1879
1880 if (instr.generates_cc) {
1881 LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code");
1882 UNREACHABLE();
1883 }
1451 break; 1884 break;
1452 } 1885 }
1453 case OpCode::Id::ISCADD_C: 1886 case OpCode::Id::ISCADD_C:
@@ -1463,6 +1896,10 @@ private:
1463 1896
1464 regs.SetRegisterToInteger(instr.gpr0, true, 0, 1897 regs.SetRegisterToInteger(instr.gpr0, true, 0,
1465 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); 1898 "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
1899 if (instr.generates_cc) {
1900 LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code");
1901 UNREACHABLE();
1902 }
1466 break; 1903 break;
1467 } 1904 }
1468 case OpCode::Id::POPC_C: 1905 case OpCode::Id::POPC_C:
@@ -1494,6 +1931,10 @@ private:
1494 1931
1495 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, 1932 WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b,
1496 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); 1933 instr.alu.lop.pred_result_mode, instr.alu.lop.pred48);
1934 if (instr.generates_cc) {
1935 LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code");
1936 UNREACHABLE();
1937 }
1497 break; 1938 break;
1498 } 1939 }
1499 case OpCode::Id::LOP3_C: 1940 case OpCode::Id::LOP3_C:
@@ -1502,13 +1943,17 @@ private:
1502 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); 1943 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39);
1503 std::string lut; 1944 std::string lut;
1504 1945
1505 if (opcode->GetId() == OpCode::Id::LOP3_R) { 1946 if (opcode->get().GetId() == OpCode::Id::LOP3_R) {
1506 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')'; 1947 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')';
1507 } else { 1948 } else {
1508 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')'; 1949 lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')';
1509 } 1950 }
1510 1951
1511 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); 1952 WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut);
1953 if (instr.generates_cc) {
1954 LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code");
1955 UNREACHABLE();
1956 }
1512 break; 1957 break;
1513 } 1958 }
1514 case OpCode::Id::IMNMX_C: 1959 case OpCode::Id::IMNMX_C:
@@ -1523,6 +1968,10 @@ private:
1523 '(' + condition + ") ? min(" + parameters + ") : max(" + 1968 '(' + condition + ") ? min(" + parameters + ") : max(" +
1524 parameters + ')', 1969 parameters + ')',
1525 1, 1); 1970 1, 1);
1971 if (instr.generates_cc) {
1972 LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code");
1973 UNREACHABLE();
1974 }
1526 break; 1975 break;
1527 } 1976 }
1528 case OpCode::Id::LEA_R2: 1977 case OpCode::Id::LEA_R2:
@@ -1532,7 +1981,7 @@ private:
1532 case OpCode::Id::LEA_HI: { 1981 case OpCode::Id::LEA_HI: {
1533 std::string op_c; 1982 std::string op_c;
1534 1983
1535 switch (opcode->GetId()) { 1984 switch (opcode->get().GetId()) {
1536 case OpCode::Id::LEA_R2: { 1985 case OpCode::Id::LEA_R2: {
1537 op_a = regs.GetRegisterAsInteger(instr.gpr20); 1986 op_a = regs.GetRegisterAsInteger(instr.gpr20);
1538 op_b = regs.GetRegisterAsInteger(instr.gpr39); 1987 op_b = regs.GetRegisterAsInteger(instr.gpr39);
@@ -1577,7 +2026,8 @@ private:
1577 op_b = regs.GetRegisterAsInteger(instr.gpr8); 2026 op_b = regs.GetRegisterAsInteger(instr.gpr8);
1578 op_a = std::to_string(instr.lea.imm.entry_a); 2027 op_a = std::to_string(instr.lea.imm.entry_a);
1579 op_c = std::to_string(instr.lea.imm.entry_b); 2028 op_c = std::to_string(instr.lea.imm.entry_b);
1580 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", opcode->GetName()); 2029 LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}",
2030 opcode->get().GetName());
1581 UNREACHABLE(); 2031 UNREACHABLE();
1582 } 2032 }
1583 } 2033 }
@@ -1592,13 +2042,95 @@ private:
1592 } 2042 }
1593 default: { 2043 default: {
1594 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", 2044 LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
1595 opcode->GetName()); 2045 opcode->get().GetName());
1596 UNREACHABLE(); 2046 UNREACHABLE();
1597 } 2047 }
1598 } 2048 }
1599 2049
1600 break; 2050 break;
1601 } 2051 }
2052 case OpCode::Type::ArithmeticHalf: {
2053 if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
2054 opcode->get().GetId() == OpCode::Id::HADD2_R) {
2055 ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented");
2056 }
2057 const bool negate_a =
2058 opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0;
2059 const bool negate_b =
2060 opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
2061
2062 const std::string op_a =
2063 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a,
2064 instr.alu_half.abs_a != 0, negate_a);
2065
2066 std::string op_b;
2067 switch (opcode->get().GetId()) {
2068 case OpCode::Id::HADD2_C:
2069 case OpCode::Id::HMUL2_C:
2070 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2071 GLSLRegister::Type::UnsignedInteger);
2072 break;
2073 case OpCode::Id::HADD2_R:
2074 case OpCode::Id::HMUL2_R:
2075 op_b = regs.GetRegisterAsInteger(instr.gpr20, 0, false);
2076 break;
2077 default:
2078 UNREACHABLE();
2079 op_b = "0";
2080 break;
2081 }
2082 op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b);
2083
2084 const std::string result = [&]() {
2085 switch (opcode->get().GetId()) {
2086 case OpCode::Id::HADD2_C:
2087 case OpCode::Id::HADD2_R:
2088 return '(' + op_a + " + " + op_b + ')';
2089 case OpCode::Id::HMUL2_C:
2090 case OpCode::Id::HMUL2_R:
2091 return '(' + op_a + " * " + op_b + ')';
2092 default:
2093 LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}",
2094 opcode->get().GetName());
2095 UNREACHABLE();
2096 return std::string("0");
2097 }
2098 }();
2099
2100 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half.merge, 1, 1,
2101 instr.alu_half.saturate != 0);
2102 break;
2103 }
2104 case OpCode::Type::ArithmeticHalfImmediate: {
2105 if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
2106 ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented");
2107 } else {
2108 ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None,
2109 "Unimplemented");
2110 }
2111
2112 const std::string op_a = GetHalfFloat(
2113 regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half_imm.type_a,
2114 instr.alu_half_imm.abs_a != 0, instr.alu_half_imm.negate_a != 0);
2115
2116 const std::string op_b = UnpackHalfImmediate(instr, true);
2117
2118 const std::string result = [&]() {
2119 switch (opcode->get().GetId()) {
2120 case OpCode::Id::HADD2_IMM:
2121 return op_a + " + " + op_b;
2122 case OpCode::Id::HMUL2_IMM:
2123 return op_a + " * " + op_b;
2124 default:
2125 UNREACHABLE();
2126 return std::string("0");
2127 }
2128 }();
2129
2130 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.alu_half_imm.merge, 1, 1,
2131 instr.alu_half_imm.saturate != 0);
2132 break;
2133 }
1602 case OpCode::Type::Ffma: { 2134 case OpCode::Type::Ffma: {
1603 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 2135 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
1604 std::string op_b = instr.ffma.negate_b ? "-" : ""; 2136 std::string op_b = instr.ffma.negate_b ? "-" : "";
@@ -1610,7 +2142,7 @@ private:
1610 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", 2142 ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented",
1611 instr.ffma.tab5980_1.Value()); 2143 instr.ffma.tab5980_1.Value());
1612 2144
1613 switch (opcode->GetId()) { 2145 switch (opcode->get().GetId()) {
1614 case OpCode::Id::FFMA_CR: { 2146 case OpCode::Id::FFMA_CR: {
1615 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 2147 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
1616 GLSLRegister::Type::Float); 2148 GLSLRegister::Type::Float);
@@ -1634,17 +2166,75 @@ private:
1634 break; 2166 break;
1635 } 2167 }
1636 default: { 2168 default: {
1637 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); 2169 LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName());
1638 UNREACHABLE(); 2170 UNREACHABLE();
1639 } 2171 }
1640 } 2172 }
1641 2173
1642 regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1, 2174 regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')',
1643 instr.alu.saturate_d); 2175 1, 1, instr.alu.saturate_d, 0, true);
2176 if (instr.generates_cc) {
2177 LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code");
2178 UNREACHABLE();
2179 }
2180
2181 break;
2182 }
2183 case OpCode::Type::Hfma2: {
2184 if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) {
2185 ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None,
2186 "Unimplemented");
2187 } else {
2188 ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None,
2189 "Unimplemented");
2190 }
2191 const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR
2192 ? instr.hfma2.rr.saturate != 0
2193 : instr.hfma2.saturate != 0;
2194
2195 const std::string op_a =
2196 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a);
2197 std::string op_b, op_c;
2198
2199 switch (opcode->get().GetId()) {
2200 case OpCode::Id::HFMA2_CR:
2201 op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2202 GLSLRegister::Type::UnsignedInteger),
2203 instr.hfma2.type_b, false, instr.hfma2.negate_b);
2204 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2205 instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
2206 break;
2207 case OpCode::Id::HFMA2_RC:
2208 op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2209 instr.hfma2.type_reg39, false, instr.hfma2.negate_b);
2210 op_c = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2211 GLSLRegister::Type::UnsignedInteger),
2212 instr.hfma2.type_b, false, instr.hfma2.negate_c);
2213 break;
2214 case OpCode::Id::HFMA2_RR:
2215 op_b = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
2216 instr.hfma2.type_b, false, instr.hfma2.negate_b);
2217 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2218 instr.hfma2.rr.type_c, false, instr.hfma2.rr.negate_c);
2219 break;
2220 case OpCode::Id::HFMA2_IMM_R:
2221 op_b = UnpackHalfImmediate(instr, true);
2222 op_c = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr39, 0, false),
2223 instr.hfma2.type_reg39, false, instr.hfma2.negate_c);
2224 break;
2225 default:
2226 UNREACHABLE();
2227 op_c = op_b = "vec2(0)";
2228 break;
2229 }
2230
2231 const std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
2232
2233 regs.SetRegisterToHalfFloat(instr.gpr0, 0, result, instr.hfma2.merge, 1, 1, saturate);
1644 break; 2234 break;
1645 } 2235 }
1646 case OpCode::Type::Conversion: { 2236 case OpCode::Type::Conversion: {
1647 switch (opcode->GetId()) { 2237 switch (opcode->get().GetId()) {
1648 case OpCode::Id::I2I_R: { 2238 case OpCode::Id::I2I_R: {
1649 ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); 2239 ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
1650 2240
@@ -1692,6 +2282,11 @@ private:
1692 } 2282 }
1693 2283
1694 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); 2284 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2285
2286 if (instr.generates_cc) {
2287 LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code");
2288 UNREACHABLE();
2289 }
1695 break; 2290 break;
1696 } 2291 }
1697 case OpCode::Id::F2F_R: { 2292 case OpCode::Id::F2F_R: {
@@ -1730,6 +2325,11 @@ private:
1730 } 2325 }
1731 2326
1732 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); 2327 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
2328
2329 if (instr.generates_cc) {
2330 LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code");
2331 UNREACHABLE();
2332 }
1733 break; 2333 break;
1734 } 2334 }
1735 case OpCode::Id::F2I_R: 2335 case OpCode::Id::F2I_R:
@@ -1779,17 +2379,22 @@ private:
1779 2379
1780 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 2380 regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1781 1, false, 0, instr.conversion.dest_size); 2381 1, false, 0, instr.conversion.dest_size);
2382 if (instr.generates_cc) {
2383 LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code");
2384 UNREACHABLE();
2385 }
1782 break; 2386 break;
1783 } 2387 }
1784 default: { 2388 default: {
1785 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); 2389 LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}",
2390 opcode->get().GetName());
1786 UNREACHABLE(); 2391 UNREACHABLE();
1787 } 2392 }
1788 } 2393 }
1789 break; 2394 break;
1790 } 2395 }
1791 case OpCode::Type::Memory: { 2396 case OpCode::Type::Memory: {
1792 switch (opcode->GetId()) { 2397 switch (opcode->get().GetId()) {
1793 case OpCode::Id::LD_A: { 2398 case OpCode::Id::LD_A: {
1794 // Note: Shouldn't this be interp mode flat? As in no interpolation made. 2399 // Note: Shouldn't this be interp mode flat? As in no interpolation made.
1795 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2400 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex,
@@ -1806,7 +2411,7 @@ private:
1806 const auto LoadNextElement = [&](u32 reg_offset) { 2411 const auto LoadNextElement = [&](u32 reg_offset) {
1807 regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, 2412 regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
1808 static_cast<Attribute::Index>(next_index), 2413 static_cast<Attribute::Index>(next_index),
1809 input_mode); 2414 input_mode, instr.gpr39.Value());
1810 2415
1811 // Load the next attribute element into the following register. If the element 2416 // Load the next attribute element into the following register. If the element
1812 // to load goes beyond the vec4 size, load the first element of the next 2417 // to load goes beyond the vec4 size, load the first element of the next
@@ -1859,6 +2464,39 @@ private:
1859 shader.AddLine("}"); 2464 shader.AddLine("}");
1860 break; 2465 break;
1861 } 2466 }
2467 case OpCode::Id::LD_L: {
2468 // Add an extra scope and declare the index register inside to prevent
2469 // overwriting it in case it is used as an output of the LD instruction.
2470 shader.AddLine('{');
2471 ++shader.scope;
2472
2473 std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " +
2474 std::to_string(instr.smem_imm.Value()) + ')';
2475
2476 shader.AddLine("uint index = (" + op + " / 4);");
2477
2478 const std::string op_a = regs.GetLocalMemoryAsFloat("index");
2479
2480 if (instr.ld_l.unknown != 1) {
2481 LOG_CRITICAL(HW_GPU, "LD_L Unhandled mode: {}",
2482 static_cast<unsigned>(instr.ld_l.unknown.Value()));
2483 UNREACHABLE();
2484 }
2485
2486 switch (instr.ldst_sl.type.Value()) {
2487 case Tegra::Shader::StoreType::Bytes32:
2488 regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
2489 break;
2490 default:
2491 LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}",
2492 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2493 UNREACHABLE();
2494 }
2495
2496 --shader.scope;
2497 shader.AddLine('}');
2498 break;
2499 }
1862 case OpCode::Id::ST_A: { 2500 case OpCode::Id::ST_A: {
1863 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, 2501 ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex,
1864 "Indirect attribute loads are not supported"); 2502 "Indirect attribute loads are not supported");
@@ -1870,8 +2508,8 @@ private:
1870 2508
1871 const auto StoreNextElement = [&](u32 reg_offset) { 2509 const auto StoreNextElement = [&](u32 reg_offset) {
1872 regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), 2510 regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index),
1873 next_element, 2511 next_element, instr.gpr0.Value() + reg_offset,
1874 instr.gpr0.Value() + reg_offset); 2512 instr.gpr39.Value());
1875 2513
1876 // Load the next attribute element into the following register. If the element 2514 // Load the next attribute element into the following register. If the element
1877 // to load goes beyond the vec4 size, load the first element of the next 2515 // to load goes beyond the vec4 size, load the first element of the next
@@ -1887,33 +2525,113 @@ private:
1887 2525
1888 break; 2526 break;
1889 } 2527 }
2528 case OpCode::Id::ST_L: {
2529 // Add an extra scope and declare the index register inside to prevent
2530 // overwriting it in case it is used as an output of the LD instruction.
2531 shader.AddLine('{');
2532 ++shader.scope;
2533
2534 std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " +
2535 std::to_string(instr.smem_imm.Value()) + ')';
2536
2537 shader.AddLine("uint index = (" + op + " / 4);");
2538
2539 if (instr.st_l.unknown != 0) {
2540 LOG_CRITICAL(HW_GPU, "ST_L Unhandled mode: {}",
2541 static_cast<unsigned>(instr.st_l.unknown.Value()));
2542 UNREACHABLE();
2543 }
2544
2545 switch (instr.ldst_sl.type.Value()) {
2546 case Tegra::Shader::StoreType::Bytes32:
2547 regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0));
2548 break;
2549 default:
2550 LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}",
2551 static_cast<unsigned>(instr.ldst_sl.type.Value()));
2552 UNREACHABLE();
2553 }
2554
2555 --shader.scope;
2556 shader.AddLine('}');
2557 break;
2558 }
1890 case OpCode::Id::TEX: { 2559 case OpCode::Id::TEX: {
1891 ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
1892 Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; 2560 Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
1893 std::string coord; 2561 std::string coord;
2562 const bool is_array = instr.tex.array != 0;
1894 2563
1895 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2564 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
1896 "NODEP is not implemented"); 2565 "NODEP is not implemented");
1897 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2566 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
1898 "AOFFI is not implemented"); 2567 "AOFFI is not implemented");
1899 ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
1900 "DC is not implemented");
1901 2568
1902 switch (texture_type) { 2569 const bool depth_compare =
1903 case Tegra::Shader::TextureType::Texture1D: { 2570 instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
1904 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2571 u32 num_coordinates = TextureCoordinates(texture_type);
1905 coord = "float coords = " + x + ';'; 2572 if (depth_compare)
2573 num_coordinates += 1;
2574
2575 switch (num_coordinates) {
2576 case 1: {
2577 if (is_array) {
2578 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2579 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2580 coord = "vec2 coords = vec2(" + x + ", " + index + ");";
2581 } else {
2582 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2583 coord = "float coords = " + x + ';';
2584 }
1906 break; 2585 break;
1907 } 2586 }
1908 case Tegra::Shader::TextureType::Texture2D: { 2587 case 2: {
1909 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2588 if (is_array) {
1910 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2589 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
1911 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2590 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2591 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2592 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
2593 } else {
2594 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2595 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2596 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2597 }
2598 break;
2599 }
2600 case 3: {
2601 if (depth_compare) {
2602 if (is_array) {
2603 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2604 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2605 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2606 const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2607 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2608 ");";
2609 } else {
2610 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2611 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2612 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2613 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2614 }
2615 } else {
2616 if (is_array) {
2617 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
2618 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2619 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2620 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3);
2621 coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
2622 ");";
2623 } else {
2624 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2625 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2626 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2627 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2628 }
2629 }
1912 break; 2630 break;
1913 } 2631 }
1914 default: 2632 default:
1915 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2633 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}",
1916 static_cast<u32>(texture_type)); 2634 static_cast<u32>(num_coordinates));
1917 UNREACHABLE(); 2635 UNREACHABLE();
1918 2636
1919 // Fallback to interpreting as a 2D texture for now 2637 // Fallback to interpreting as a 2D texture for now
@@ -1924,9 +2642,10 @@ private:
1924 } 2642 }
1925 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias 2643 // TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias
1926 // or lod. 2644 // or lod.
1927 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20); 2645 std::string op_c;
1928 2646
1929 const std::string sampler = GetSampler(instr.sampler, texture_type, false); 2647 const std::string sampler =
2648 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
1930 // Add an extra scope and declare the texture coords inside to prevent 2649 // Add an extra scope and declare the texture coords inside to prevent
1931 // overwriting them in case they are used as outputs of the texs instruction. 2650 // overwriting them in case they are used as outputs of the texs instruction.
1932 2651
@@ -1935,7 +2654,7 @@ private:
1935 shader.AddLine(coord); 2654 shader.AddLine(coord);
1936 std::string texture; 2655 std::string texture;
1937 2656
1938 switch (instr.tex.process_mode) { 2657 switch (instr.tex.GetTextureProcessMode()) {
1939 case Tegra::Shader::TextureProcessMode::None: { 2658 case Tegra::Shader::TextureProcessMode::None: {
1940 texture = "texture(" + sampler + ", coords)"; 2659 texture = "texture(" + sampler + ", coords)";
1941 break; 2660 break;
@@ -1946,12 +2665,25 @@ private:
1946 } 2665 }
1947 case Tegra::Shader::TextureProcessMode::LB: 2666 case Tegra::Shader::TextureProcessMode::LB:
1948 case Tegra::Shader::TextureProcessMode::LBA: { 2667 case Tegra::Shader::TextureProcessMode::LBA: {
2668 if (depth_compare) {
2669 if (is_array)
2670 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2);
2671 else
2672 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2673 } else {
2674 op_c = regs.GetRegisterAsFloat(instr.gpr20);
2675 }
1949 // TODO: Figure if A suffix changes the equation at all. 2676 // TODO: Figure if A suffix changes the equation at all.
1950 texture = "texture(" + sampler + ", coords, " + op_c + ')'; 2677 texture = "texture(" + sampler + ", coords, " + op_c + ')';
1951 break; 2678 break;
1952 } 2679 }
1953 case Tegra::Shader::TextureProcessMode::LL: 2680 case Tegra::Shader::TextureProcessMode::LL:
1954 case Tegra::Shader::TextureProcessMode::LLA: { 2681 case Tegra::Shader::TextureProcessMode::LLA: {
2682 if (num_coordinates <= 2) {
2683 op_c = regs.GetRegisterAsFloat(instr.gpr20);
2684 } else {
2685 op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2686 }
1955 // TODO: Figure if A suffix changes the equation at all. 2687 // TODO: Figure if A suffix changes the equation at all.
1956 texture = "textureLod(" + sampler + ", coords, " + op_c + ')'; 2688 texture = "textureLod(" + sampler + ", coords, " + op_c + ')';
1957 break; 2689 break;
@@ -1959,18 +2691,22 @@ private:
1959 default: { 2691 default: {
1960 texture = "texture(" + sampler + ", coords)"; 2692 texture = "texture(" + sampler + ", coords)";
1961 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}", 2693 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}",
1962 static_cast<u32>(instr.tex.process_mode.Value())); 2694 static_cast<u32>(instr.tex.GetTextureProcessMode()));
1963 UNREACHABLE(); 2695 UNREACHABLE();
1964 } 2696 }
1965 } 2697 }
1966 std::size_t dest_elem{}; 2698 if (!depth_compare) {
1967 for (std::size_t elem = 0; elem < 4; ++elem) { 2699 std::size_t dest_elem{};
1968 if (!instr.tex.IsComponentEnabled(elem)) { 2700 for (std::size_t elem = 0; elem < 4; ++elem) {
1969 // Skip disabled components 2701 if (!instr.tex.IsComponentEnabled(elem)) {
1970 continue; 2702 // Skip disabled components
2703 continue;
2704 }
2705 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
2706 ++dest_elem;
1971 } 2707 }
1972 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); 2708 } else {
1973 ++dest_elem; 2709 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
1974 } 2710 }
1975 --shader.scope; 2711 --shader.scope;
1976 shader.AddLine("}"); 2712 shader.AddLine("}");
@@ -1983,11 +2719,15 @@ private:
1983 2719
1984 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), 2720 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
1985 "NODEP is not implemented"); 2721 "NODEP is not implemented");
1986 ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
1987 "DC is not implemented");
1988 2722
1989 switch (texture_type) { 2723 const bool depth_compare =
1990 case Tegra::Shader::TextureType::Texture2D: { 2724 instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2725 u32 num_coordinates = TextureCoordinates(texture_type);
2726 if (depth_compare)
2727 num_coordinates += 1;
2728
2729 switch (num_coordinates) {
2730 case 2: {
1991 if (is_array) { 2731 if (is_array) {
1992 const std::string index = regs.GetRegisterAsInteger(instr.gpr8); 2732 const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
1993 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2733 const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -2000,17 +2740,25 @@ private:
2000 } 2740 }
2001 break; 2741 break;
2002 } 2742 }
2003 case Tegra::Shader::TextureType::TextureCube: { 2743 case 3: {
2004 ASSERT_MSG(!is_array, "Unimplemented"); 2744 if (is_array) {
2005 std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2745 UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented");
2006 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2746 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2007 std::string z = regs.GetRegisterAsFloat(instr.gpr20); 2747 const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
2008 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; 2748 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2749 texture_type = Tegra::Shader::TextureType::Texture2D;
2750 is_array = false;
2751 } else {
2752 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2753 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2754 const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
2755 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2756 }
2009 break; 2757 break;
2010 } 2758 }
2011 default: 2759 default:
2012 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2760 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}",
2013 static_cast<u32>(texture_type)); 2761 static_cast<u32>(num_coordinates));
2014 UNREACHABLE(); 2762 UNREACHABLE();
2015 2763
2016 // Fallback to interpreting as a 2D texture for now 2764 // Fallback to interpreting as a 2D texture for now
@@ -2020,9 +2768,35 @@ private:
2020 texture_type = Tegra::Shader::TextureType::Texture2D; 2768 texture_type = Tegra::Shader::TextureType::Texture2D;
2021 is_array = false; 2769 is_array = false;
2022 } 2770 }
2023 const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); 2771 const std::string sampler =
2024 const std::string texture = "texture(" + sampler + ", coords)"; 2772 GetSampler(instr.sampler, texture_type, is_array, depth_compare);
2025 WriteTexsInstruction(instr, coord, texture); 2773 std::string texture;
2774 switch (instr.texs.GetTextureProcessMode()) {
2775 case Tegra::Shader::TextureProcessMode::None: {
2776 texture = "texture(" + sampler + ", coords)";
2777 break;
2778 }
2779 case Tegra::Shader::TextureProcessMode::LZ: {
2780 texture = "textureLod(" + sampler + ", coords, 0.0)";
2781 break;
2782 }
2783 case Tegra::Shader::TextureProcessMode::LL: {
2784 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
2785 texture = "textureLod(" + sampler + ", coords, " + op_c + ')';
2786 break;
2787 }
2788 default: {
2789 texture = "texture(" + sampler + ", coords)";
2790 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}",
2791 static_cast<u32>(instr.texs.GetTextureProcessMode()));
2792 UNREACHABLE();
2793 }
2794 }
2795 if (!depth_compare) {
2796 WriteTexsInstruction(instr, coord, texture);
2797 } else {
2798 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')');
2799 }
2026 break; 2800 break;
2027 } 2801 }
2028 case OpCode::Id::TLDS: { 2802 case OpCode::Id::TLDS: {
@@ -2040,6 +2814,8 @@ private:
2040 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), 2814 ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ),
2041 "MZ is not implemented"); 2815 "MZ is not implemented");
2042 2816
2817 u32 op_c_offset = 0;
2818
2043 switch (texture_type) { 2819 switch (texture_type) {
2044 case Tegra::Shader::TextureType::Texture1D: { 2820 case Tegra::Shader::TextureType::Texture1D: {
2045 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2821 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
@@ -2054,6 +2830,7 @@ private:
2054 const std::string x = regs.GetRegisterAsInteger(instr.gpr8); 2830 const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
2055 const std::string y = regs.GetRegisterAsInteger(instr.gpr20); 2831 const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
2056 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");"; 2832 coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
2833 op_c_offset = 1;
2057 } 2834 }
2058 break; 2835 break;
2059 } 2836 }
@@ -2062,9 +2839,27 @@ private:
2062 static_cast<u32>(texture_type)); 2839 static_cast<u32>(texture_type));
2063 UNREACHABLE(); 2840 UNREACHABLE();
2064 } 2841 }
2065 2842 const std::string sampler =
2066 const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); 2843 GetSampler(instr.sampler, texture_type, is_array, false);
2067 const std::string texture = "texelFetch(" + sampler + ", coords, 0)"; 2844 std::string texture = "texelFetch(" + sampler + ", coords, 0)";
2845 switch (instr.tlds.GetTextureProcessMode()) {
2846 case Tegra::Shader::TextureProcessMode::LZ: {
2847 texture = "texelFetch(" + sampler + ", coords, 0)";
2848 break;
2849 }
2850 case Tegra::Shader::TextureProcessMode::LL: {
2851 const std::string op_c =
2852 regs.GetRegisterAsInteger(instr.gpr20.Value() + op_c_offset);
2853 texture = "texelFetch(" + sampler + ", coords, " + op_c + ')';
2854 break;
2855 }
2856 default: {
2857 texture = "texelFetch(" + sampler + ", coords, 0)";
2858 LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}",
2859 static_cast<u32>(instr.tlds.GetTextureProcessMode()));
2860 UNREACHABLE();
2861 }
2862 }
2068 WriteTexsInstruction(instr, coord, texture); 2863 WriteTexsInstruction(instr, coord, texture);
2069 break; 2864 break;
2070 } 2865 }
@@ -2077,28 +2872,43 @@ private:
2077 "NODEP is not implemented"); 2872 "NODEP is not implemented");
2078 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2873 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2079 "AOFFI is not implemented"); 2874 "AOFFI is not implemented");
2080 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
2081 "DC is not implemented");
2082 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2875 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2083 "NDV is not implemented"); 2876 "NDV is not implemented");
2084 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), 2877 ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
2085 "PTP is not implemented"); 2878 "PTP is not implemented");
2086 2879 const bool depth_compare =
2087 switch (instr.tld4.texture_type) { 2880 instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2088 case Tegra::Shader::TextureType::Texture2D: { 2881 auto texture_type = instr.tld4.texture_type.Value();
2882 u32 num_coordinates = TextureCoordinates(texture_type);
2883 if (depth_compare)
2884 num_coordinates += 1;
2885
2886 switch (num_coordinates) {
2887 case 2: {
2089 const std::string x = regs.GetRegisterAsFloat(instr.gpr8); 2888 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2090 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); 2889 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2091 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 2890 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2092 break; 2891 break;
2093 } 2892 }
2893 case 3: {
2894 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2895 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2896 const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
2897 coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
2898 break;
2899 }
2094 default: 2900 default:
2095 LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", 2901 LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}",
2096 static_cast<u32>(instr.tld4.texture_type.Value())); 2902 static_cast<u32>(num_coordinates));
2097 UNREACHABLE(); 2903 UNREACHABLE();
2904 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2905 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2906 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2907 texture_type = Tegra::Shader::TextureType::Texture2D;
2098 } 2908 }
2099 2909
2100 const std::string sampler = 2910 const std::string sampler =
2101 GetSampler(instr.sampler, instr.tld4.texture_type, false); 2911 GetSampler(instr.sampler, texture_type, false, depth_compare);
2102 // Add an extra scope and declare the texture coords inside to prevent 2912 // Add an extra scope and declare the texture coords inside to prevent
2103 // overwriting them in case they are used as outputs of the texs instruction. 2913 // overwriting them in case they are used as outputs of the texs instruction.
2104 shader.AddLine("{"); 2914 shader.AddLine("{");
@@ -2106,15 +2916,18 @@ private:
2106 shader.AddLine(coord); 2916 shader.AddLine(coord);
2107 const std::string texture = "textureGather(" + sampler + ", coords, " + 2917 const std::string texture = "textureGather(" + sampler + ", coords, " +
2108 std::to_string(instr.tld4.component) + ')'; 2918 std::to_string(instr.tld4.component) + ')';
2109 2919 if (!depth_compare) {
2110 std::size_t dest_elem{}; 2920 std::size_t dest_elem{};
2111 for (std::size_t elem = 0; elem < 4; ++elem) { 2921 for (std::size_t elem = 0; elem < 4; ++elem) {
2112 if (!instr.tex.IsComponentEnabled(elem)) { 2922 if (!instr.tex.IsComponentEnabled(elem)) {
2113 // Skip disabled components 2923 // Skip disabled components
2114 continue; 2924 continue;
2925 }
2926 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
2927 ++dest_elem;
2115 } 2928 }
2116 regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); 2929 } else {
2117 ++dest_elem; 2930 regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
2118 } 2931 }
2119 --shader.scope; 2932 --shader.scope;
2120 shader.AddLine("}"); 2933 shader.AddLine("}");
@@ -2125,18 +2938,30 @@ private:
2125 "NODEP is not implemented"); 2938 "NODEP is not implemented");
2126 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), 2939 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
2127 "AOFFI is not implemented"); 2940 "AOFFI is not implemented");
2128 ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
2129 "DC is not implemented");
2130 2941
2942 const bool depth_compare =
2943 instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
2131 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 2944 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
2132 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); 2945 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
2133 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. 2946 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
2134 const std::string sampler = 2947 const std::string sampler = GetSampler(
2135 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false); 2948 instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare);
2136 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; 2949 std::string coord;
2950 if (!depth_compare) {
2951 coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
2952 } else {
2953 // Note: TLD4S coordinate encoding works just like TEXS's
2954 const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2955 coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");";
2956 }
2137 const std::string texture = "textureGather(" + sampler + ", coords, " + 2957 const std::string texture = "textureGather(" + sampler + ", coords, " +
2138 std::to_string(instr.tld4s.component) + ')'; 2958 std::to_string(instr.tld4s.component) + ')';
2139 WriteTexsInstruction(instr, coord, texture); 2959
2960 if (!depth_compare) {
2961 WriteTexsInstruction(instr, coord, texture);
2962 } else {
2963 WriteTexsInstruction(instr, coord, "vec4(" + texture + ')');
2964 }
2140 break; 2965 break;
2141 } 2966 }
2142 case OpCode::Id::TXQ: { 2967 case OpCode::Id::TXQ: {
@@ -2147,7 +2972,7 @@ private:
2147 // Sadly, not all texture instructions specify the type of texture their sampler 2972 // Sadly, not all texture instructions specify the type of texture their sampler
2148 // uses. This must be fixed at a later instance. 2973 // uses. This must be fixed at a later instance.
2149 const std::string sampler = 2974 const std::string sampler =
2150 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false); 2975 GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
2151 switch (instr.txq.query_type) { 2976 switch (instr.txq.query_type) {
2152 case Tegra::Shader::TextureQueryType::Dimension: { 2977 case Tegra::Shader::TextureQueryType::Dimension: {
2153 const std::string texture = "textureQueryLevels(" + sampler + ')'; 2978 const std::string texture = "textureQueryLevels(" + sampler + ')';
@@ -2168,24 +2993,22 @@ private:
2168 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), 2993 ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
2169 "NDV is not implemented"); 2994 "NDV is not implemented");
2170 2995
2171 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); 2996 const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2172 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2173 const bool is_array = instr.tmml.array != 0; 2997 const bool is_array = instr.tmml.array != 0;
2174 auto texture_type = instr.tmml.texture_type.Value(); 2998 auto texture_type = instr.tmml.texture_type.Value();
2175 const std::string sampler = GetSampler(instr.sampler, texture_type, is_array); 2999 const std::string sampler =
3000 GetSampler(instr.sampler, texture_type, is_array, false);
2176 3001
2177 // TODO: add coordinates for different samplers once other texture types are 3002 // TODO: add coordinates for different samplers once other texture types are
2178 // implemented. 3003 // implemented.
2179 std::string coord; 3004 std::string coord;
2180 switch (texture_type) { 3005 switch (texture_type) {
2181 case Tegra::Shader::TextureType::Texture1D: { 3006 case Tegra::Shader::TextureType::Texture1D: {
2182 std::string x = regs.GetRegisterAsFloat(instr.gpr8);
2183 coord = "float coords = " + x + ';'; 3007 coord = "float coords = " + x + ';';
2184 break; 3008 break;
2185 } 3009 }
2186 case Tegra::Shader::TextureType::Texture2D: { 3010 case Tegra::Shader::TextureType::Texture2D: {
2187 std::string x = regs.GetRegisterAsFloat(instr.gpr8); 3011 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2188 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2189 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 3012 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2190 break; 3013 break;
2191 } 3014 }
@@ -2195,8 +3018,7 @@ private:
2195 UNREACHABLE(); 3018 UNREACHABLE();
2196 3019
2197 // Fallback to interpreting as a 2D texture for now 3020 // Fallback to interpreting as a 2D texture for now
2198 std::string x = regs.GetRegisterAsFloat(instr.gpr8); 3021 const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2199 std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
2200 coord = "vec2 coords = vec2(" + x + ", " + y + ");"; 3022 coord = "vec2 coords = vec2(" + x + ", " + y + ");";
2201 texture_type = Tegra::Shader::TextureType::Texture2D; 3023 texture_type = Tegra::Shader::TextureType::Texture2D;
2202 } 3024 }
@@ -2216,27 +3038,20 @@ private:
2216 break; 3038 break;
2217 } 3039 }
2218 default: { 3040 default: {
2219 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); 3041 LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName());
2220 UNREACHABLE(); 3042 UNREACHABLE();
2221 } 3043 }
2222 } 3044 }
2223 break; 3045 break;
2224 } 3046 }
2225 case OpCode::Type::FloatSetPredicate: { 3047 case OpCode::Type::FloatSetPredicate: {
2226 std::string op_a = instr.fsetp.neg_a ? "-" : ""; 3048 const std::string op_a =
2227 op_a += regs.GetRegisterAsFloat(instr.gpr8); 3049 GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8), instr.fsetp.abs_a != 0,
2228 3050 instr.fsetp.neg_a != 0);
2229 if (instr.fsetp.abs_a) {
2230 op_a = "abs(" + op_a + ')';
2231 }
2232 3051
2233 std::string op_b{}; 3052 std::string op_b;
2234 3053
2235 if (instr.is_b_imm) { 3054 if (instr.is_b_imm) {
2236 if (instr.fsetp.neg_b) {
2237 // Only the immediate version of fsetp has a neg_b bit.
2238 op_b += '-';
2239 }
2240 op_b += '(' + GetImmediate19(instr) + ')'; 3055 op_b += '(' + GetImmediate19(instr) + ')';
2241 } else { 3056 } else {
2242 if (instr.is_b_gpr) { 3057 if (instr.is_b_gpr) {
@@ -2309,6 +3124,51 @@ private:
2309 } 3124 }
2310 break; 3125 break;
2311 } 3126 }
3127 case OpCode::Type::HalfSetPredicate: {
3128 ASSERT_MSG(instr.hsetp2.ftz == 0, "Unimplemented");
3129
3130 const std::string op_a =
3131 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hsetp2.type_a,
3132 instr.hsetp2.abs_a, instr.hsetp2.negate_a);
3133
3134 const std::string op_b = [&]() {
3135 switch (opcode->get().GetId()) {
3136 case OpCode::Id::HSETP2_R:
3137 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3138 instr.hsetp2.type_b, instr.hsetp2.abs_a,
3139 instr.hsetp2.negate_b);
3140 default:
3141 UNREACHABLE();
3142 return std::string("vec2(0)");
3143 }
3144 }();
3145
3146 // We can't use the constant predicate as destination.
3147 ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex));
3148
3149 const std::string second_pred =
3150 GetPredicateCondition(instr.hsetp2.pred39, instr.hsetp2.neg_pred != 0);
3151
3152 const std::string combiner = GetPredicateCombiner(instr.hsetp2.op);
3153
3154 const std::string component_combiner = instr.hsetp2.h_and ? "&&" : "||";
3155 const std::string predicate =
3156 '(' + GetPredicateComparison(instr.hsetp2.cond, op_a + ".x", op_b + ".x") + ' ' +
3157 component_combiner + ' ' +
3158 GetPredicateComparison(instr.hsetp2.cond, op_a + ".y", op_b + ".y") + ')';
3159
3160 // Set the primary predicate to the result of Predicate OP SecondPredicate
3161 SetPredicate(instr.hsetp2.pred3,
3162 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
3163
3164 if (instr.hsetp2.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3165 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
3166 // if enabled
3167 SetPredicate(instr.hsetp2.pred0,
3168 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
3169 }
3170 break;
3171 }
2312 case OpCode::Type::PredicateSetRegister: { 3172 case OpCode::Type::PredicateSetRegister: {
2313 const std::string op_a = 3173 const std::string op_a =
2314 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0); 3174 GetPredicateCondition(instr.pset.pred12, instr.pset.neg_pred12 != 0);
@@ -2331,10 +3191,15 @@ private:
2331 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); 3191 regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1);
2332 } 3192 }
2333 3193
3194 if (instr.generates_cc) {
3195 LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code");
3196 UNREACHABLE();
3197 }
3198
2334 break; 3199 break;
2335 } 3200 }
2336 case OpCode::Type::PredicateSetPredicate: { 3201 case OpCode::Type::PredicateSetPredicate: {
2337 switch (opcode->GetId()) { 3202 switch (opcode->get().GetId()) {
2338 case OpCode::Id::PSETP: { 3203 case OpCode::Id::PSETP: {
2339 const std::string op_a = 3204 const std::string op_a =
2340 GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); 3205 GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0);
@@ -2368,52 +3233,44 @@ private:
2368 const std::string pred = 3233 const std::string pred =
2369 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 3234 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2370 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 3235 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2371 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 3236 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2372 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 3237 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2373 SetPredicate(instr.csetp.pred3, 3238 SetPredicate(instr.csetp.pred3,
2374 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 3239 '(' + control_code + ") " + combiner + " (" + pred + ')');
2375 } 3240 }
2376 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 3241 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2377 SetPredicate(instr.csetp.pred0, 3242 SetPredicate(instr.csetp.pred0,
2378 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 3243 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2379 } 3244 }
2380 break; 3245 break;
2381 } 3246 }
2382 default: { 3247 default: {
2383 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName()); 3248 LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}",
3249 opcode->get().GetName());
2384 UNREACHABLE(); 3250 UNREACHABLE();
2385 } 3251 }
2386 } 3252 }
2387 break; 3253 break;
2388 } 3254 }
2389 case OpCode::Type::FloatSet: { 3255 case OpCode::Type::FloatSet: {
2390 std::string op_a = instr.fset.neg_a ? "-" : ""; 3256 const std::string op_a = GetOperandAbsNeg(regs.GetRegisterAsFloat(instr.gpr8),
2391 op_a += regs.GetRegisterAsFloat(instr.gpr8); 3257 instr.fset.abs_a != 0, instr.fset.neg_a != 0);
2392
2393 if (instr.fset.abs_a) {
2394 op_a = "abs(" + op_a + ')';
2395 }
2396 3258
2397 std::string op_b = instr.fset.neg_b ? "-" : ""; 3259 std::string op_b;
2398 3260
2399 if (instr.is_b_imm) { 3261 if (instr.is_b_imm) {
2400 const std::string imm = GetImmediate19(instr); 3262 const std::string imm = GetImmediate19(instr);
2401 if (instr.fset.neg_imm) 3263 op_b = imm;
2402 op_b += "(-" + imm + ')';
2403 else
2404 op_b += imm;
2405 } else { 3264 } else {
2406 if (instr.is_b_gpr) { 3265 if (instr.is_b_gpr) {
2407 op_b += regs.GetRegisterAsFloat(instr.gpr20); 3266 op_b = regs.GetRegisterAsFloat(instr.gpr20);
2408 } else { 3267 } else {
2409 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 3268 op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
2410 GLSLRegister::Type::Float); 3269 GLSLRegister::Type::Float);
2411 } 3270 }
2412 } 3271 }
2413 3272
2414 if (instr.fset.abs_b) { 3273 op_b = GetOperandAbsNeg(op_b, instr.fset.abs_b != 0, instr.fset.neg_b != 0);
2415 op_b = "abs(" + op_b + ')';
2416 }
2417 3274
2418 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the 3275 // The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
2419 // condition is true, and to 0 otherwise. 3276 // condition is true, and to 0 otherwise.
@@ -2469,6 +3326,50 @@ private:
2469 } 3326 }
2470 break; 3327 break;
2471 } 3328 }
3329 case OpCode::Type::HalfSet: {
3330 ASSERT_MSG(instr.hset2.ftz == 0, "Unimplemented");
3331
3332 const std::string op_a =
3333 GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hset2.type_a,
3334 instr.hset2.abs_a != 0, instr.hset2.negate_a != 0);
3335
3336 const std::string op_b = [&]() {
3337 switch (opcode->get().GetId()) {
3338 case OpCode::Id::HSET2_R:
3339 return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false),
3340 instr.hset2.type_b, instr.hset2.abs_b != 0,
3341 instr.hset2.negate_b != 0);
3342 default:
3343 UNREACHABLE();
3344 return std::string("vec2(0)");
3345 }
3346 }();
3347
3348 const std::string second_pred =
3349 GetPredicateCondition(instr.hset2.pred39, instr.hset2.neg_pred != 0);
3350
3351 const std::string combiner = GetPredicateCombiner(instr.hset2.op);
3352
3353 // HSET2 operates on each half float in the pack.
3354 std::string result;
3355 for (int i = 0; i < 2; ++i) {
3356 const std::string float_value = i == 0 ? "0x00003c00" : "0x3c000000";
3357 const std::string integer_value = i == 0 ? "0x0000ffff" : "0xffff0000";
3358 const std::string value = instr.hset2.bf == 1 ? float_value : integer_value;
3359
3360 const std::string comp = std::string(".") + "xy"[i];
3361 const std::string predicate =
3362 "((" + GetPredicateComparison(instr.hset2.cond, op_a + comp, op_b + comp) +
3363 ") " + combiner + " (" + second_pred + "))";
3364
3365 result += '(' + predicate + " ? " + value + " : 0)";
3366 if (i == 0) {
3367 result += " | ";
3368 }
3369 }
3370 regs.SetRegisterToInteger(instr.gpr0, false, 0, '(' + result + ')', 1, 1);
3371 break;
3372 }
2472 case OpCode::Type::Xmad: { 3373 case OpCode::Type::Xmad: {
2473 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented"); 3374 ASSERT_MSG(!instr.xmad.sign_a, "Unimplemented");
2474 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented"); 3375 ASSERT_MSG(!instr.xmad.sign_b, "Unimplemented");
@@ -2482,7 +3383,7 @@ private:
2482 const bool is_signed{instr.xmad.sign_a == 1}; 3383 const bool is_signed{instr.xmad.sign_a == 1};
2483 3384
2484 bool is_merge{}; 3385 bool is_merge{};
2485 switch (opcode->GetId()) { 3386 switch (opcode->get().GetId()) {
2486 case OpCode::Id::XMAD_CR: { 3387 case OpCode::Id::XMAD_CR: {
2487 is_merge = instr.xmad.merge_56; 3388 is_merge = instr.xmad.merge_56;
2488 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, 3389 op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
@@ -2511,7 +3412,7 @@ private:
2511 break; 3412 break;
2512 } 3413 }
2513 default: { 3414 default: {
2514 LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->GetName()); 3415 LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName());
2515 UNREACHABLE(); 3416 UNREACHABLE();
2516 } 3417 }
2517 } 3418 }
@@ -2560,15 +3461,25 @@ private:
2560 } 3461 }
2561 3462
2562 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); 3463 regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1);
3464 if (instr.generates_cc) {
3465 LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code");
3466 UNREACHABLE();
3467 }
2563 break; 3468 break;
2564 } 3469 }
2565 default: { 3470 default: {
2566 switch (opcode->GetId()) { 3471 switch (opcode->get().GetId()) {
2567 case OpCode::Id::EXIT: { 3472 case OpCode::Id::EXIT: {
2568 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { 3473 if (stage == Maxwell3D::Regs::ShaderStage::Fragment) {
2569 EmitFragmentOutputsWrite(); 3474 EmitFragmentOutputsWrite();
2570 } 3475 }
2571 3476
3477 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3478 if (cc != Tegra::Shader::ControlCode::T) {
3479 LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc));
3480 UNREACHABLE();
3481 }
3482
2572 switch (instr.flow.cond) { 3483 switch (instr.flow.cond) {
2573 case Tegra::Shader::FlowCondition::Always: 3484 case Tegra::Shader::FlowCondition::Always:
2574 shader.AddLine("return true;"); 3485 shader.AddLine("return true;");
@@ -2598,6 +3509,11 @@ private:
2598 3509
2599 // Enclose "discard" in a conditional, so that GLSL compilation does not complain 3510 // Enclose "discard" in a conditional, so that GLSL compilation does not complain
2600 // about unexecuted instructions that may follow this. 3511 // about unexecuted instructions that may follow this.
3512 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3513 if (cc != Tegra::Shader::ControlCode::T) {
3514 LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc));
3515 UNREACHABLE();
3516 }
2601 shader.AddLine("if (true) {"); 3517 shader.AddLine("if (true) {");
2602 ++shader.scope; 3518 ++shader.scope;
2603 shader.AddLine("discard;"); 3519 shader.AddLine("discard;");
@@ -2606,9 +3522,60 @@ private:
2606 3522
2607 break; 3523 break;
2608 } 3524 }
3525 case OpCode::Id::OUT_R: {
3526 ASSERT(instr.gpr20.Value() == Register::ZeroIndex);
3527 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3528 "OUT is expected to be used in a geometry shader.");
3529
3530 if (instr.out.emit) {
3531 // gpr0 is used to store the next address. Hardware returns a pointer but
3532 // we just return the next index with a cyclic cap.
3533 const std::string current{regs.GetRegisterAsInteger(instr.gpr8, 0, false)};
3534 const std::string next = "((" + current + " + 1" + ") % " +
3535 std::to_string(MAX_GEOMETRY_BUFFERS) + ')';
3536 shader.AddLine("emit_vertex(" + current + ");");
3537 regs.SetRegisterToInteger(instr.gpr0, false, 0, next, 1, 1);
3538 }
3539 if (instr.out.cut) {
3540 shader.AddLine("EndPrimitive();");
3541 }
3542
3543 break;
3544 }
3545 case OpCode::Id::MOV_SYS: {
3546 switch (instr.sys20) {
3547 case Tegra::Shader::SystemVariable::InvocationInfo: {
3548 LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
3549 regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1);
3550 break;
3551 }
3552 default: {
3553 LOG_CRITICAL(HW_GPU, "Unhandled system move: {}",
3554 static_cast<u32>(instr.sys20.Value()));
3555 UNREACHABLE();
3556 }
3557 }
3558 break;
3559 }
3560 case OpCode::Id::ISBERD: {
3561 ASSERT(instr.isberd.o == 0);
3562 ASSERT(instr.isberd.skew == 0);
3563 ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None);
3564 ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None);
3565 ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
3566 "ISBERD is expected to be used in a geometry shader.");
3567 LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
3568 regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8), 1, 1);
3569 break;
3570 }
2609 case OpCode::Id::BRA: { 3571 case OpCode::Id::BRA: {
2610 ASSERT_MSG(instr.bra.constant_buffer == 0, 3572 ASSERT_MSG(instr.bra.constant_buffer == 0,
2611 "BRA with constant buffers are not implemented"); 3573 "BRA with constant buffers are not implemented");
3574 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3575 if (cc != Tegra::Shader::ControlCode::T) {
3576 LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc));
3577 UNREACHABLE();
3578 }
2612 const u32 target = offset + instr.bra.GetBranchTarget(); 3579 const u32 target = offset + instr.bra.GetBranchTarget();
2613 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }"); 3580 shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
2614 break; 3581 break;
@@ -2631,16 +3598,40 @@ private:
2631 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it 3598 // The SSY opcode tells the GPU where to re-converge divergent execution paths, it
2632 // sets the target of the jump that the SYNC instruction will make. The SSY opcode 3599 // sets the target of the jump that the SYNC instruction will make. The SSY opcode
2633 // has a similar structure to the BRA opcode. 3600 // has a similar structure to the BRA opcode.
2634 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported"); 3601 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer flow is not supported");
3602
3603 const u32 target = offset + instr.bra.GetBranchTarget();
3604 EmitPushToFlowStack(target);
3605 break;
3606 }
3607 case OpCode::Id::PBK: {
3608 // PBK pushes to a stack the address where BRK will jump to. This shares stack with
3609 // SSY but using SYNC on a PBK address will kill the shader execution. We don't
3610 // emulate this because it's very unlikely a driver will emit such invalid shader.
3611 ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer PBK is not supported");
2635 3612
2636 const u32 target = offset + instr.bra.GetBranchTarget(); 3613 const u32 target = offset + instr.bra.GetBranchTarget();
2637 EmitPushToSSYStack(target); 3614 EmitPushToFlowStack(target);
2638 break; 3615 break;
2639 } 3616 }
2640 case OpCode::Id::SYNC: { 3617 case OpCode::Id::SYNC: {
2641 // The SYNC opcode jumps to the address previously set by the SSY opcode 3618 // The SYNC opcode jumps to the address previously set by the SSY opcode
2642 ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always); 3619 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
2643 EmitPopFromSSYStack(); 3620 if (cc != Tegra::Shader::ControlCode::T) {
3621 LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc));
3622 UNREACHABLE();
3623 }
3624 EmitPopFromFlowStack();
3625 break;
3626 }
3627 case OpCode::Id::BRK: {
3628 // The BRK opcode jumps to the address previously set by the PBK opcode
3629 const Tegra::Shader::ControlCode cc = instr.flow_control_code;
3630 if (cc != Tegra::Shader::ControlCode::T) {
3631 LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc));
3632 UNREACHABLE();
3633 }
3634 EmitPopFromFlowStack();
2644 break; 3635 break;
2645 } 3636 }
2646 case OpCode::Id::DEPBAR: { 3637 case OpCode::Id::DEPBAR: {
@@ -2649,8 +3640,59 @@ private:
2649 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); 3640 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
2650 break; 3641 break;
2651 } 3642 }
3643 case OpCode::Id::VMAD: {
3644 const bool result_signed = instr.video.signed_a == 1 || instr.video.signed_b == 1;
3645 const std::string op_a = GetVideoOperandA(instr);
3646 const std::string op_b = GetVideoOperandB(instr);
3647 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed);
3648
3649 std::string result = '(' + op_a + " * " + op_b + " + " + op_c + ')';
3650
3651 switch (instr.vmad.shr) {
3652 case Tegra::Shader::VmadShr::Shr7:
3653 result = '(' + result + " >> 7)";
3654 break;
3655 case Tegra::Shader::VmadShr::Shr15:
3656 result = '(' + result + " >> 15)";
3657 break;
3658 }
3659
3660 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1,
3661 instr.vmad.saturate == 1, 0, Register::Size::Word,
3662 instr.vmad.cc);
3663 if (instr.generates_cc) {
3664 LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code");
3665 UNREACHABLE();
3666 }
3667
3668 break;
3669 }
3670 case OpCode::Id::VSETP: {
3671 const std::string op_a = GetVideoOperandA(instr);
3672 const std::string op_b = GetVideoOperandB(instr);
3673
3674 // We can't use the constant predicate as destination.
3675 ASSERT(instr.vsetp.pred3 != static_cast<u64>(Pred::UnusedIndex));
3676
3677 const std::string second_pred = GetPredicateCondition(instr.vsetp.pred39, false);
3678
3679 const std::string combiner = GetPredicateCombiner(instr.vsetp.op);
3680
3681 const std::string predicate = GetPredicateComparison(instr.vsetp.cond, op_a, op_b);
3682 // Set the primary predicate to the result of Predicate OP SecondPredicate
3683 SetPredicate(instr.vsetp.pred3,
3684 '(' + predicate + ") " + combiner + " (" + second_pred + ')');
3685
3686 if (instr.vsetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
3687 // Set the secondary predicate to the result of !Predicate OP SecondPredicate,
3688 // if enabled
3689 SetPredicate(instr.vsetp.pred0,
3690 "!(" + predicate + ") " + combiner + " (" + second_pred + ')');
3691 }
3692 break;
3693 }
2652 default: { 3694 default: {
2653 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); 3695 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName());
2654 UNREACHABLE(); 3696 UNREACHABLE();
2655 } 3697 }
2656 } 3698 }
@@ -2712,11 +3754,11 @@ private:
2712 labels.insert(subroutine.begin); 3754 labels.insert(subroutine.begin);
2713 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;"); 3755 shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
2714 3756
2715 // TODO(Subv): Figure out the actual depth of the SSY stack, for now it seems 3757 // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
2716 // unlikely that shaders will use 20 nested SSYs. 3758 // unlikely that shaders will use 20 nested SSYs and PBKs.
2717 constexpr u32 SSY_STACK_SIZE = 20; 3759 constexpr u32 FLOW_STACK_SIZE = 20;
2718 shader.AddLine("uint ssy_stack[" + std::to_string(SSY_STACK_SIZE) + "];"); 3760 shader.AddLine("uint flow_stack[" + std::to_string(FLOW_STACK_SIZE) + "];");
2719 shader.AddLine("uint ssy_stack_top = 0u;"); 3761 shader.AddLine("uint flow_stack_top = 0u;");
2720 3762
2721 shader.AddLine("while (true) {"); 3763 shader.AddLine("while (true) {");
2722 ++shader.scope; 3764 ++shader.scope;
@@ -2776,23 +3818,24 @@ private:
2776 const u32 main_offset; 3818 const u32 main_offset;
2777 Maxwell3D::Regs::ShaderStage stage; 3819 Maxwell3D::Regs::ShaderStage stage;
2778 const std::string& suffix; 3820 const std::string& suffix;
3821 u64 local_memory_size;
2779 3822
2780 ShaderWriter shader; 3823 ShaderWriter shader;
2781 ShaderWriter declarations; 3824 ShaderWriter declarations;
2782 GLSLRegisterManager regs{shader, declarations, stage, suffix}; 3825 GLSLRegisterManager regs{shader, declarations, stage, suffix, header};
2783 3826
2784 // Declarations 3827 // Declarations
2785 std::set<std::string> declr_predicates; 3828 std::set<std::string> declr_predicates;
2786}; // namespace Decompiler 3829}; // namespace OpenGL::GLShader::Decompiler
2787 3830
2788std::string GetCommonDeclarations() { 3831std::string GetCommonDeclarations() {
2789 return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n", 3832 return fmt::format("#define MAX_CONSTBUFFER_ELEMENTS {}\n",
2790 RasterizerOpenGL::MaxConstbufferSize / sizeof(GLvec4)); 3833 RasterizerOpenGL::MaxConstbufferSize / sizeof(GLvec4));
2791} 3834}
2792 3835
2793boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, 3836std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
2794 Maxwell3D::Regs::ShaderStage stage, 3837 Maxwell3D::Regs::ShaderStage stage,
2795 const std::string& suffix) { 3838 const std::string& suffix) {
2796 try { 3839 try {
2797 const auto subroutines = 3840 const auto subroutines =
2798 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); 3841 ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines();
@@ -2801,7 +3844,7 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
2801 } catch (const DecompileFail& exception) { 3844 } catch (const DecompileFail& exception) {
2802 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); 3845 LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what());
2803 } 3846 }
2804 return boost::none; 3847 return {};
2805} 3848}
2806 3849
2807} // namespace OpenGL::GLShader::Decompiler 3850} // namespace OpenGL::GLShader::Decompiler
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index b20cc4bfa..d01a4a7ee 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -6,8 +6,8 @@
6 6
7#include <array> 7#include <array>
8#include <functional> 8#include <functional>
9#include <optional>
9#include <string> 10#include <string>
10#include <boost/optional.hpp>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "video_core/engines/maxwell_3d.h" 12#include "video_core/engines/maxwell_3d.h"
13#include "video_core/renderer_opengl/gl_shader_gen.h" 13#include "video_core/renderer_opengl/gl_shader_gen.h"
@@ -18,8 +18,8 @@ using Tegra::Engines::Maxwell3D;
18 18
19std::string GetCommonDeclarations(); 19std::string GetCommonDeclarations();
20 20
21boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, 21std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset,
22 Maxwell3D::Regs::ShaderStage stage, 22 Maxwell3D::Regs::ShaderStage stage,
23 const std::string& suffix); 23 const std::string& suffix);
24 24
25} // namespace OpenGL::GLShader::Decompiler 25} // namespace OpenGL::GLShader::Decompiler
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index b0466c18f..9d17edd63 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -17,7 +17,18 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
17 std::string out = "#version 430 core\n"; 17 std::string out = "#version 430 core\n";
18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; 18 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
19 out += Decompiler::GetCommonDeclarations(); 19 out += Decompiler::GetCommonDeclarations();
20 out += "bool exec_vertex();\n"; 20
21 out += R"(
22
23layout (location = 0) out vec4 position;
24
25layout(std140) uniform vs_config {
26 vec4 viewport_flip;
27 uvec4 instance_id;
28 uvec4 flip_stage;
29 uvec4 alpha_test;
30};
31)";
21 32
22 if (setup.IsDualProgram()) { 33 if (setup.IsDualProgram()) {
23 out += "bool exec_vertex_b();\n"; 34 out += "bool exec_vertex_b();\n";
@@ -26,20 +37,19 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
26 ProgramResult program = 37 ProgramResult program =
27 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, 38 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
28 Maxwell3D::Regs::ShaderStage::Vertex, "vertex") 39 Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
29 .get_value_or({}); 40 .value_or(ProgramResult());
30 41
31 out += R"( 42 out += program.first;
32
33out gl_PerVertex {
34 vec4 gl_Position;
35};
36 43
37out vec4 position; 44 if (setup.IsDualProgram()) {
45 ProgramResult program_b =
46 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
47 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
48 .value_or(ProgramResult());
49 out += program_b.first;
50 }
38 51
39layout (std140) uniform vs_config { 52 out += R"(
40 vec4 viewport_flip;
41 uvec4 instance_id;
42};
43 53
44void main() { 54void main() {
45 position = vec4(0.0, 0.0, 0.0, 0.0); 55 position = vec4(0.0, 0.0, 0.0, 0.0);
@@ -52,27 +62,56 @@ void main() {
52 62
53 out += R"( 63 out += R"(
54 64
55 // Viewport can be flipped, which is unsupported by glViewport 65 // Check if the flip stage is VertexB
56 position.xy *= viewport_flip.xy; 66 if (flip_stage[0] == 1) {
67 // Viewport can be flipped, which is unsupported by glViewport
68 position.xy *= viewport_flip.xy;
69 }
57 gl_Position = position; 70 gl_Position = position;
58 71
59 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0 72 // TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
60 // For now, this is here to bring order in lieu of proper emulation 73 // For now, this is here to bring order in lieu of proper emulation
61 position.w = 1.0; 74 if (flip_stage[0] == 1) {
75 position.w = 1.0;
76 }
62} 77}
63 78
64)"; 79)";
65 80
66 out += program.first; 81 return {out, program.second};
82}
67 83
68 if (setup.IsDualProgram()) { 84ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
69 ProgramResult program_b = 85 std::string out = "#version 430 core\n";
70 Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET, 86 out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
71 Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b") 87 out += Decompiler::GetCommonDeclarations();
72 .get_value_or({}); 88 out += "bool exec_geometry();\n";
73 out += program_b.first; 89
74 } 90 ProgramResult program =
91 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
92 Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
93 .value_or(ProgramResult());
94 out += R"(
95out gl_PerVertex {
96 vec4 gl_Position;
97};
75 98
99layout (location = 0) in vec4 gs_position[];
100layout (location = 0) out vec4 position;
101
102layout (std140) uniform gs_config {
103 vec4 viewport_flip;
104 uvec4 instance_id;
105 uvec4 flip_stage;
106 uvec4 alpha_test;
107};
108
109void main() {
110 exec_geometry();
111}
112
113)";
114 out += program.first;
76 return {out, program.second}; 115 return {out, program.second};
77} 116}
78 117
@@ -85,9 +124,8 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
85 ProgramResult program = 124 ProgramResult program =
86 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, 125 Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
87 Maxwell3D::Regs::ShaderStage::Fragment, "fragment") 126 Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
88 .get_value_or({}); 127 .value_or(ProgramResult());
89 out += R"( 128 out += R"(
90in vec4 position;
91layout(location = 0) out vec4 FragColor0; 129layout(location = 0) out vec4 FragColor0;
92layout(location = 1) out vec4 FragColor1; 130layout(location = 1) out vec4 FragColor1;
93layout(location = 2) out vec4 FragColor2; 131layout(location = 2) out vec4 FragColor2;
@@ -97,11 +135,39 @@ layout(location = 5) out vec4 FragColor5;
97layout(location = 6) out vec4 FragColor6; 135layout(location = 6) out vec4 FragColor6;
98layout(location = 7) out vec4 FragColor7; 136layout(location = 7) out vec4 FragColor7;
99 137
138layout (location = 0) in vec4 position;
139
100layout (std140) uniform fs_config { 140layout (std140) uniform fs_config {
101 vec4 viewport_flip; 141 vec4 viewport_flip;
102 uvec4 instance_id; 142 uvec4 instance_id;
143 uvec4 flip_stage;
144 uvec4 alpha_test;
103}; 145};
104 146
147bool AlphaFunc(in float value) {
148 float ref = uintBitsToFloat(alpha_test[2]);
149 switch (alpha_test[1]) {
150 case 1:
151 return false;
152 case 2:
153 return value < ref;
154 case 3:
155 return value == ref;
156 case 4:
157 return value <= ref;
158 case 5:
159 return value > ref;
160 case 6:
161 return value != ref;
162 case 7:
163 return value >= ref;
164 case 8:
165 return true;
166 default:
167 return false;
168 }
169}
170
105void main() { 171void main() {
106 exec_fragment(); 172 exec_fragment();
107} 173}
@@ -110,5 +176,4 @@ void main() {
110 out += program.first; 176 out += program.first;
111 return {out, program.second}; 177 return {out, program.second};
112} 178}
113
114} // namespace OpenGL::GLShader 179} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index d53b93ad5..520b9d4e3 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -16,6 +16,8 @@ namespace OpenGL::GLShader {
16constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; 16constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000};
17using ProgramCode = std::vector<u64>; 17using ProgramCode = std::vector<u64>;
18 18
19enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
20
19class ConstBufferEntry { 21class ConstBufferEntry {
20 using Maxwell = Tegra::Engines::Maxwell3D::Regs; 22 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
21 23
@@ -75,8 +77,9 @@ class SamplerEntry {
75 77
76public: 78public:
77 SamplerEntry(Maxwell::ShaderStage stage, std::size_t offset, std::size_t index, 79 SamplerEntry(Maxwell::ShaderStage stage, std::size_t offset, std::size_t index,
78 Tegra::Shader::TextureType type, bool is_array) 80 Tegra::Shader::TextureType type, bool is_array, bool is_shadow)
79 : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {} 81 : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array),
82 is_shadow(is_shadow) {}
80 83
81 std::size_t GetOffset() const { 84 std::size_t GetOffset() const {
82 return offset; 85 return offset;
@@ -117,6 +120,8 @@ public:
117 } 120 }
118 if (is_array) 121 if (is_array)
119 glsl_type += "Array"; 122 glsl_type += "Array";
123 if (is_shadow)
124 glsl_type += "Shadow";
120 return glsl_type; 125 return glsl_type;
121 } 126 }
122 127
@@ -128,6 +133,10 @@ public:
128 return is_array; 133 return is_array;
129 } 134 }
130 135
136 bool IsShadow() const {
137 return is_shadow;
138 }
139
131 u32 GetHash() const { 140 u32 GetHash() const {
132 return (static_cast<u32>(stage) << 16) | static_cast<u32>(sampler_index); 141 return (static_cast<u32>(stage) << 16) | static_cast<u32>(sampler_index);
133 } 142 }
@@ -147,7 +156,8 @@ private:
147 Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. 156 Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
148 std::size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. 157 std::size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
149 Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc) 158 Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
150 bool is_array; ///< Whether the texture is being sampled as an array texture or not. 159 bool is_array; ///< Whether the texture is being sampled as an array texture or not.
160 bool is_shadow; ///< Whether the texture is being sampled as a depth texture or not.
151}; 161};
152 162
153struct ShaderEntries { 163struct ShaderEntries {
@@ -188,6 +198,12 @@ private:
188ProgramResult GenerateVertexShader(const ShaderSetup& setup); 198ProgramResult GenerateVertexShader(const ShaderSetup& setup);
189 199
190/** 200/**
201 * Generates the GLSL geometry shader program source code for the given GS program
202 * @returns String of the shader source code
203 */
204ProgramResult GenerateGeometryShader(const ShaderSetup& setup);
205
206/**
191 * Generates the GLSL fragment shader program source code for the given FS program 207 * Generates the GLSL fragment shader program source code for the given FS program
192 * @returns String of the shader source code 208 * @returns String of the shader source code
193 */ 209 */
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 022d32a86..8b8869ecb 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -16,8 +16,27 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; 16 viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; 17 viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
18 18
19 u32 func = static_cast<u32>(regs.alpha_test_func);
20 // Normalize the gl variants of opCompare to be the same as the normal variants
21 u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never);
22 if (func >= op_gl_variant_base) {
23 func = func - op_gl_variant_base + 1U;
24 }
25
26 alpha_test.enabled = regs.alpha_test_enabled;
27 alpha_test.func = func;
28 alpha_test.ref = regs.alpha_test_ref;
29
19 // We only assign the instance to the first component of the vector, the rest is just padding. 30 // We only assign the instance to the first component of the vector, the rest is just padding.
20 instance_id[0] = state.current_instance; 31 instance_id[0] = state.current_instance;
32
33 // Assign in which stage the position has to be flipped
34 // (the last stage before the fragment shader).
35 if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
36 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
37 } else {
38 flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
39 }
21} 40}
22 41
23} // namespace OpenGL::GLShader 42} // namespace OpenGL::GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 3de15ba9b..36fe1f04c 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,8 +21,15 @@ struct MaxwellUniformData {
21 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage); 21 void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
22 alignas(16) GLvec4 viewport_flip; 22 alignas(16) GLvec4 viewport_flip;
23 alignas(16) GLuvec4 instance_id; 23 alignas(16) GLuvec4 instance_id;
24 alignas(16) GLuvec4 flip_stage;
25 struct alignas(16) {
26 GLuint enabled;
27 GLuint func;
28 GLfloat ref;
29 GLuint padding;
30 } alpha_test;
24}; 31};
25static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect"); 32static_assert(sizeof(MaxwellUniformData) == 64, "MaxwellUniformData structure size is incorrect");
26static_assert(sizeof(MaxwellUniformData) < 16384, 33static_assert(sizeof(MaxwellUniformData) < 16384,
27 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); 34 "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
28 35
@@ -36,6 +43,10 @@ public:
36 vs = program; 43 vs = program;
37 } 44 }
38 45
46 void UseProgrammableGeometryShader(GLuint program) {
47 gs = program;
48 }
49
39 void UseProgrammableFragmentShader(GLuint program) { 50 void UseProgrammableFragmentShader(GLuint program) {
40 fs = program; 51 fs = program;
41 } 52 }
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 1fe26a2a9..b6b426f34 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -11,9 +11,10 @@
11namespace OpenGL { 11namespace OpenGL {
12 12
13OpenGLState OpenGLState::cur_state; 13OpenGLState OpenGLState::cur_state;
14 14bool OpenGLState::s_rgb_used;
15OpenGLState::OpenGLState() { 15OpenGLState::OpenGLState() {
16 // These all match default OpenGL values 16 // These all match default OpenGL values
17 framebuffer_srgb.enabled = false;
17 cull.enabled = false; 18 cull.enabled = false;
18 cull.mode = GL_BACK; 19 cull.mode = GL_BACK;
19 cull.front_face = GL_CCW; 20 cull.front_face = GL_CCW;
@@ -21,6 +22,11 @@ OpenGLState::OpenGLState() {
21 depth.test_enabled = false; 22 depth.test_enabled = false;
22 depth.test_func = GL_LESS; 23 depth.test_func = GL_LESS;
23 depth.write_mask = GL_TRUE; 24 depth.write_mask = GL_TRUE;
25 depth.depth_range_near = 0.0f;
26 depth.depth_range_far = 1.0f;
27
28 primitive_restart.enabled = false;
29 primitive_restart.index = 0;
24 30
25 color_mask.red_enabled = GL_TRUE; 31 color_mask.red_enabled = GL_TRUE;
26 color_mask.green_enabled = GL_TRUE; 32 color_mask.green_enabled = GL_TRUE;
@@ -83,79 +89,144 @@ OpenGLState::OpenGLState() {
83 point.size = 1; 89 point.size = 1;
84} 90}
85 91
86void OpenGLState::Apply() const { 92void OpenGLState::ApplyDefaultState() {
93 glDisable(GL_FRAMEBUFFER_SRGB);
94 glDisable(GL_CULL_FACE);
95 glDisable(GL_DEPTH_TEST);
96 glDisable(GL_PRIMITIVE_RESTART);
97 glDisable(GL_STENCIL_TEST);
98 glEnable(GL_BLEND);
99 glDisable(GL_COLOR_LOGIC_OP);
100 glDisable(GL_SCISSOR_TEST);
101}
102
103void OpenGLState::ApplySRgb() const {
104 // sRGB
105 if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
106 if (framebuffer_srgb.enabled) {
107 // Track if sRGB is used
108 s_rgb_used = true;
109 glEnable(GL_FRAMEBUFFER_SRGB);
110 } else {
111 glDisable(GL_FRAMEBUFFER_SRGB);
112 }
113 }
114}
115
116void OpenGLState::ApplyCulling() const {
87 // Culling 117 // Culling
88 if (cull.enabled != cur_state.cull.enabled) { 118 const bool cull_changed = cull.enabled != cur_state.cull.enabled;
119 if (cull_changed) {
89 if (cull.enabled) { 120 if (cull.enabled) {
90 glEnable(GL_CULL_FACE); 121 glEnable(GL_CULL_FACE);
91 } else { 122 } else {
92 glDisable(GL_CULL_FACE); 123 glDisable(GL_CULL_FACE);
93 } 124 }
94 } 125 }
126 if (cull.enabled) {
127 if (cull_changed || cull.mode != cur_state.cull.mode) {
128 glCullFace(cull.mode);
129 }
95 130
96 if (cull.mode != cur_state.cull.mode) { 131 if (cull_changed || cull.front_face != cur_state.cull.front_face) {
97 glCullFace(cull.mode); 132 glFrontFace(cull.front_face);
98 } 133 }
99
100 if (cull.front_face != cur_state.cull.front_face) {
101 glFrontFace(cull.front_face);
102 } 134 }
135}
103 136
137void OpenGLState::ApplyDepth() const {
104 // Depth test 138 // Depth test
105 if (depth.test_enabled != cur_state.depth.test_enabled) { 139 const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
140 if (depth_test_changed) {
106 if (depth.test_enabled) { 141 if (depth.test_enabled) {
107 glEnable(GL_DEPTH_TEST); 142 glEnable(GL_DEPTH_TEST);
108 } else { 143 } else {
109 glDisable(GL_DEPTH_TEST); 144 glDisable(GL_DEPTH_TEST);
110 } 145 }
111 } 146 }
112 147 if (depth.test_enabled &&
113 if (depth.test_func != cur_state.depth.test_func) { 148 (depth_test_changed || depth.test_func != cur_state.depth.test_func)) {
114 glDepthFunc(depth.test_func); 149 glDepthFunc(depth.test_func);
115 } 150 }
116
117 // Depth mask 151 // Depth mask
118 if (depth.write_mask != cur_state.depth.write_mask) { 152 if (depth.write_mask != cur_state.depth.write_mask) {
119 glDepthMask(depth.write_mask); 153 glDepthMask(depth.write_mask);
120 } 154 }
155 // Depth range
156 if (depth.depth_range_near != cur_state.depth.depth_range_near ||
157 depth.depth_range_far != cur_state.depth.depth_range_far) {
158 glDepthRange(depth.depth_range_near, depth.depth_range_far);
159 }
160}
121 161
122 // Color mask 162void OpenGLState::ApplyPrimitiveRestart() const {
123 if (color_mask.red_enabled != cur_state.color_mask.red_enabled || 163 const bool primitive_restart_changed =
124 color_mask.green_enabled != cur_state.color_mask.green_enabled || 164 primitive_restart.enabled != cur_state.primitive_restart.enabled;
125 color_mask.blue_enabled != cur_state.color_mask.blue_enabled || 165 if (primitive_restart_changed) {
126 color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) { 166 if (primitive_restart.enabled) {
127 glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled, 167 glEnable(GL_PRIMITIVE_RESTART);
128 color_mask.alpha_enabled); 168 } else {
169 glDisable(GL_PRIMITIVE_RESTART);
170 }
171 }
172 if (primitive_restart_changed ||
173 (primitive_restart.enabled &&
174 primitive_restart.index != cur_state.primitive_restart.index)) {
175 glPrimitiveRestartIndex(primitive_restart.index);
129 } 176 }
177}
130 178
131 // Stencil test 179void OpenGLState::ApplyStencilTest() const {
132 if (stencil.test_enabled != cur_state.stencil.test_enabled) { 180 const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled;
181 if (stencil_test_changed) {
133 if (stencil.test_enabled) { 182 if (stencil.test_enabled) {
134 glEnable(GL_STENCIL_TEST); 183 glEnable(GL_STENCIL_TEST);
135 } else { 184 } else {
136 glDisable(GL_STENCIL_TEST); 185 glDisable(GL_STENCIL_TEST);
137 } 186 }
138 } 187 }
139 auto config_stencil = [](GLenum face, const auto& config, const auto& prev_config) { 188 if (stencil.test_enabled) {
140 if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || 189 auto config_stencil = [stencil_test_changed](GLenum face, const auto& config,
141 config.test_mask != prev_config.test_mask) { 190 const auto& prev_config) {
142 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); 191 if (stencil_test_changed || config.test_func != prev_config.test_func ||
143 } 192 config.test_ref != prev_config.test_ref ||
144 if (config.action_depth_fail != prev_config.action_depth_fail || 193 config.test_mask != prev_config.test_mask) {
145 config.action_depth_pass != prev_config.action_depth_pass || 194 glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
146 config.action_stencil_fail != prev_config.action_stencil_fail) { 195 }
147 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, 196 if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail ||
148 config.action_depth_pass); 197 config.action_depth_pass != prev_config.action_depth_pass ||
149 } 198 config.action_stencil_fail != prev_config.action_stencil_fail) {
150 if (config.write_mask != prev_config.write_mask) { 199 glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
151 glStencilMaskSeparate(face, config.write_mask); 200 config.action_depth_pass);
201 }
202 if (config.write_mask != prev_config.write_mask) {
203 glStencilMaskSeparate(face, config.write_mask);
204 }
205 };
206 config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front);
207 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
208 }
209}
210
211void OpenGLState::ApplyScissorTest() const {
212 const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
213 if (scissor_changed) {
214 if (scissor.enabled) {
215 glEnable(GL_SCISSOR_TEST);
216 } else {
217 glDisable(GL_SCISSOR_TEST);
152 } 218 }
153 }; 219 }
154 config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); 220 if (scissor_changed || scissor_changed || scissor.x != cur_state.scissor.x ||
155 config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); 221 scissor.y != cur_state.scissor.y || scissor.width != cur_state.scissor.width ||
222 scissor.height != cur_state.scissor.height) {
223 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
224 }
225}
156 226
157 // Blending 227void OpenGLState::ApplyBlending() const {
158 if (blend.enabled != cur_state.blend.enabled) { 228 const bool blend_changed = blend.enabled != cur_state.blend.enabled;
229 if (blend_changed) {
159 if (blend.enabled) { 230 if (blend.enabled) {
160 ASSERT(!logic_op.enabled); 231 ASSERT(!logic_op.enabled);
161 glEnable(GL_BLEND); 232 glEnable(GL_BLEND);
@@ -163,29 +234,32 @@ void OpenGLState::Apply() const {
163 glDisable(GL_BLEND); 234 glDisable(GL_BLEND);
164 } 235 }
165 } 236 }
237 if (blend.enabled) {
238 if (blend_changed || blend.color.red != cur_state.blend.color.red ||
239 blend.color.green != cur_state.blend.color.green ||
240 blend.color.blue != cur_state.blend.color.blue ||
241 blend.color.alpha != cur_state.blend.color.alpha) {
242 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha);
243 }
166 244
167 if (blend.color.red != cur_state.blend.color.red || 245 if (blend_changed || blend.src_rgb_func != cur_state.blend.src_rgb_func ||
168 blend.color.green != cur_state.blend.color.green || 246 blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
169 blend.color.blue != cur_state.blend.color.blue || 247 blend.src_a_func != cur_state.blend.src_a_func ||
170 blend.color.alpha != cur_state.blend.color.alpha) { 248 blend.dst_a_func != cur_state.blend.dst_a_func) {
171 glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); 249 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
172 } 250 blend.dst_a_func);
173 251 }
174 if (blend.src_rgb_func != cur_state.blend.src_rgb_func ||
175 blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
176 blend.src_a_func != cur_state.blend.src_a_func ||
177 blend.dst_a_func != cur_state.blend.dst_a_func) {
178 glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
179 blend.dst_a_func);
180 }
181 252
182 if (blend.rgb_equation != cur_state.blend.rgb_equation || 253 if (blend_changed || blend.rgb_equation != cur_state.blend.rgb_equation ||
183 blend.a_equation != cur_state.blend.a_equation) { 254 blend.a_equation != cur_state.blend.a_equation) {
184 glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); 255 glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
256 }
185 } 257 }
258}
186 259
187 // Logic Operation 260void OpenGLState::ApplyLogicOp() const {
188 if (logic_op.enabled != cur_state.logic_op.enabled) { 261 const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled;
262 if (logic_op_changed) {
189 if (logic_op.enabled) { 263 if (logic_op.enabled) {
190 ASSERT(!blend.enabled); 264 ASSERT(!blend.enabled);
191 glEnable(GL_COLOR_LOGIC_OP); 265 glEnable(GL_COLOR_LOGIC_OP);
@@ -194,11 +268,13 @@ void OpenGLState::Apply() const {
194 } 268 }
195 } 269 }
196 270
197 if (logic_op.operation != cur_state.logic_op.operation) { 271 if (logic_op.enabled &&
272 (logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) {
198 glLogicOp(logic_op.operation); 273 glLogicOp(logic_op.operation);
199 } 274 }
275}
200 276
201 // Textures 277void OpenGLState::ApplyTextures() const {
202 for (std::size_t i = 0; i < std::size(texture_units); ++i) { 278 for (std::size_t i = 0; i < std::size(texture_units); ++i) {
203 const auto& texture_unit = texture_units[i]; 279 const auto& texture_unit = texture_units[i];
204 const auto& cur_state_texture_unit = cur_state.texture_units[i]; 280 const auto& cur_state_texture_unit = cur_state.texture_units[i];
@@ -217,28 +293,29 @@ void OpenGLState::Apply() const {
217 glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); 293 glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
218 } 294 }
219 } 295 }
296}
220 297
221 // Samplers 298void OpenGLState::ApplySamplers() const {
222 { 299 bool has_delta{};
223 bool has_delta{}; 300 std::size_t first{}, last{};
224 std::size_t first{}, last{}; 301 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers;
225 std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; 302 for (std::size_t i = 0; i < std::size(samplers); ++i) {
226 for (std::size_t i = 0; i < std::size(samplers); ++i) { 303 samplers[i] = texture_units[i].sampler;
227 samplers[i] = texture_units[i].sampler; 304 if (samplers[i] != cur_state.texture_units[i].sampler) {
228 if (samplers[i] != cur_state.texture_units[i].sampler) { 305 if (!has_delta) {
229 if (!has_delta) { 306 first = i;
230 first = i; 307 has_delta = true;
231 has_delta = true;
232 }
233 last = i;
234 } 308 }
235 } 309 last = i;
236 if (has_delta) {
237 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
238 samplers.data());
239 } 310 }
240 } 311 }
312 if (has_delta) {
313 glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
314 samplers.data());
315 }
316}
241 317
318void OpenGLState::Apply() const {
242 // Framebuffer 319 // Framebuffer
243 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 320 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
244 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 321 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -271,27 +348,12 @@ void OpenGLState::Apply() const {
271 if (draw.program_pipeline != cur_state.draw.program_pipeline) { 348 if (draw.program_pipeline != cur_state.draw.program_pipeline) {
272 glBindProgramPipeline(draw.program_pipeline); 349 glBindProgramPipeline(draw.program_pipeline);
273 } 350 }
274 351 // Viewport
275 // Scissor test
276 if (scissor.enabled != cur_state.scissor.enabled) {
277 if (scissor.enabled) {
278 glEnable(GL_SCISSOR_TEST);
279 } else {
280 glDisable(GL_SCISSOR_TEST);
281 }
282 }
283
284 if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
285 scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
286 glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
287 }
288
289 if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y || 352 if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
290 viewport.width != cur_state.viewport.width || 353 viewport.width != cur_state.viewport.width ||
291 viewport.height != cur_state.viewport.height) { 354 viewport.height != cur_state.viewport.height) {
292 glViewport(viewport.x, viewport.y, viewport.width, viewport.height); 355 glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
293 } 356 }
294
295 // Clip distance 357 // Clip distance
296 for (std::size_t i = 0; i < clip_distance.size(); ++i) { 358 for (std::size_t i = 0; i < clip_distance.size(); ++i) {
297 if (clip_distance[i] != cur_state.clip_distance[i]) { 359 if (clip_distance[i] != cur_state.clip_distance[i]) {
@@ -302,12 +364,28 @@ void OpenGLState::Apply() const {
302 } 364 }
303 } 365 }
304 } 366 }
305 367 // Color mask
368 if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
369 color_mask.green_enabled != cur_state.color_mask.green_enabled ||
370 color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
371 color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
372 glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
373 color_mask.alpha_enabled);
374 }
306 // Point 375 // Point
307 if (point.size != cur_state.point.size) { 376 if (point.size != cur_state.point.size) {
308 glPointSize(point.size); 377 glPointSize(point.size);
309 } 378 }
310 379 ApplyScissorTest();
380 ApplyStencilTest();
381 ApplySRgb();
382 ApplyCulling();
383 ApplyDepth();
384 ApplyPrimitiveRestart();
385 ApplyBlending();
386 ApplyLogicOp();
387 ApplyTextures();
388 ApplySamplers();
311 cur_state = *this; 389 cur_state = *this;
312} 390}
313 391
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index dc21a2ee3..fe648aff6 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -36,18 +36,29 @@ constexpr TextureUnit ProcTexDiffLUT{9};
36class OpenGLState { 36class OpenGLState {
37public: 37public:
38 struct { 38 struct {
39 bool enabled; // GL_FRAMEBUFFER_SRGB
40 } framebuffer_srgb;
41
42 struct {
39 bool enabled; // GL_CULL_FACE 43 bool enabled; // GL_CULL_FACE
40 GLenum mode; // GL_CULL_FACE_MODE 44 GLenum mode; // GL_CULL_FACE_MODE
41 GLenum front_face; // GL_FRONT_FACE 45 GLenum front_face; // GL_FRONT_FACE
42 } cull; 46 } cull;
43 47
44 struct { 48 struct {
45 bool test_enabled; // GL_DEPTH_TEST 49 bool test_enabled; // GL_DEPTH_TEST
46 GLenum test_func; // GL_DEPTH_FUNC 50 GLenum test_func; // GL_DEPTH_FUNC
47 GLboolean write_mask; // GL_DEPTH_WRITEMASK 51 GLboolean write_mask; // GL_DEPTH_WRITEMASK
52 GLfloat depth_range_near; // GL_DEPTH_RANGE
53 GLfloat depth_range_far; // GL_DEPTH_RANGE
48 } depth; 54 } depth;
49 55
50 struct { 56 struct {
57 bool enabled;
58 GLuint index;
59 } primitive_restart; // GL_PRIMITIVE_RESTART
60
61 struct {
51 GLboolean red_enabled; 62 GLboolean red_enabled;
52 GLboolean green_enabled; 63 GLboolean green_enabled;
53 GLboolean blue_enabled; 64 GLboolean blue_enabled;
@@ -154,10 +165,16 @@ public:
154 static OpenGLState GetCurState() { 165 static OpenGLState GetCurState() {
155 return cur_state; 166 return cur_state;
156 } 167 }
157 168 static bool GetsRGBUsed() {
169 return s_rgb_used;
170 }
171 static void ClearsRGBUsed() {
172 s_rgb_used = false;
173 }
158 /// Apply this state as the current OpenGL state 174 /// Apply this state as the current OpenGL state
159 void Apply() const; 175 void Apply() const;
160 176 /// Set the initial OpenGL state
177 static void ApplyDefaultState();
161 /// Resets any references to the given resource 178 /// Resets any references to the given resource
162 OpenGLState& UnbindTexture(GLuint handle); 179 OpenGLState& UnbindTexture(GLuint handle);
163 OpenGLState& ResetSampler(GLuint handle); 180 OpenGLState& ResetSampler(GLuint handle);
@@ -169,6 +186,19 @@ public:
169 186
170private: 187private:
171 static OpenGLState cur_state; 188 static OpenGLState cur_state;
189 // Workaround for sRGB problems caused by
190 // QT not supporting srgb output
191 static bool s_rgb_used;
192 void ApplySRgb() const;
193 void ApplyCulling() const;
194 void ApplyDepth() const;
195 void ApplyPrimitiveRestart() const;
196 void ApplyStencilTest() const;
197 void ApplyScissorTest() const;
198 void ApplyBlending() const;
199 void ApplyLogicOp() const;
200 void ApplyTextures() const;
201 void ApplySamplers() const;
172}; 202};
173 203
174} // namespace OpenGL 204} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 67273e164..87d511c38 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -82,8 +82,20 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
82 return {}; 82 return {};
83 } 83 }
84 84
85 case Maxwell::VertexAttribute::Type::Float: 85 case Maxwell::VertexAttribute::Type::Float: {
86 return GL_FLOAT; 86 switch (attrib.size) {
87 case Maxwell::VertexAttribute::Size::Size_16:
88 case Maxwell::VertexAttribute::Size::Size_16_16:
89 case Maxwell::VertexAttribute::Size::Size_16_16_16:
90 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
91 return GL_HALF_FLOAT;
92 case Maxwell::VertexAttribute::Size::Size_32:
93 case Maxwell::VertexAttribute::Size::Size_32_32:
94 case Maxwell::VertexAttribute::Size::Size_32_32_32:
95 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
96 return GL_FLOAT;
97 }
98 }
87 } 99 }
88 100
89 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); 101 LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
@@ -123,12 +135,29 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
123 return {}; 135 return {};
124} 136}
125 137
126inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { 138inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
139 Tegra::Texture::TextureMipmapFilter mip_filter_mode) {
127 switch (filter_mode) { 140 switch (filter_mode) {
128 case Tegra::Texture::TextureFilter::Linear: 141 case Tegra::Texture::TextureFilter::Linear: {
129 return GL_LINEAR; 142 switch (mip_filter_mode) {
130 case Tegra::Texture::TextureFilter::Nearest: 143 case Tegra::Texture::TextureMipmapFilter::None:
131 return GL_NEAREST; 144 return GL_LINEAR;
145 case Tegra::Texture::TextureMipmapFilter::Nearest:
146 return GL_NEAREST_MIPMAP_LINEAR;
147 case Tegra::Texture::TextureMipmapFilter::Linear:
148 return GL_LINEAR_MIPMAP_LINEAR;
149 }
150 }
151 case Tegra::Texture::TextureFilter::Nearest: {
152 switch (mip_filter_mode) {
153 case Tegra::Texture::TextureMipmapFilter::None:
154 return GL_NEAREST;
155 case Tegra::Texture::TextureMipmapFilter::Nearest:
156 return GL_NEAREST_MIPMAP_NEAREST;
157 case Tegra::Texture::TextureMipmapFilter::Linear:
158 return GL_LINEAR_MIPMAP_NEAREST;
159 }
160 }
132 } 161 }
133 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", 162 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}",
134 static_cast<u32>(filter_mode)); 163 static_cast<u32>(filter_mode));
@@ -159,6 +188,31 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
159 return {}; 188 return {};
160} 189}
161 190
191inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
192 switch (func) {
193 case Tegra::Texture::DepthCompareFunc::Never:
194 return GL_NEVER;
195 case Tegra::Texture::DepthCompareFunc::Less:
196 return GL_LESS;
197 case Tegra::Texture::DepthCompareFunc::LessEqual:
198 return GL_LEQUAL;
199 case Tegra::Texture::DepthCompareFunc::Equal:
200 return GL_EQUAL;
201 case Tegra::Texture::DepthCompareFunc::NotEqual:
202 return GL_NOTEQUAL;
203 case Tegra::Texture::DepthCompareFunc::Greater:
204 return GL_GREATER;
205 case Tegra::Texture::DepthCompareFunc::GreaterEqual:
206 return GL_GEQUAL;
207 case Tegra::Texture::DepthCompareFunc::Always:
208 return GL_ALWAYS;
209 }
210 LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}",
211 static_cast<u32>(func));
212 UNREACHABLE();
213 return {};
214}
215
162inline GLenum BlendEquation(Maxwell::Blend::Equation equation) { 216inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
163 switch (equation) { 217 switch (equation) {
164 case Maxwell::Blend::Equation::Add: 218 case Maxwell::Blend::Equation::Add:
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 96d916b07..ea38da932 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -115,7 +115,8 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window)
115RendererOpenGL::~RendererOpenGL() = default; 115RendererOpenGL::~RendererOpenGL() = default;
116 116
117/// Swap buffers (render frame) 117/// Swap buffers (render frame)
118void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { 118void RendererOpenGL::SwapBuffers(
119 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
119 ScopeAcquireGLContext acquire_context{render_window}; 120 ScopeAcquireGLContext acquire_context{render_window};
120 121
121 Core::System::GetInstance().GetPerfStats().EndSystemFrame(); 122 Core::System::GetInstance().GetPerfStats().EndSystemFrame();
@@ -124,11 +125,11 @@ void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&
124 OpenGLState prev_state = OpenGLState::GetCurState(); 125 OpenGLState prev_state = OpenGLState::GetCurState();
125 state.Apply(); 126 state.Apply();
126 127
127 if (framebuffer != boost::none) { 128 if (framebuffer) {
128 // If framebuffer is provided, reload it from memory to a texture 129 // If framebuffer is provided, reload it from memory to a texture
129 if (screen_info.texture.width != (GLsizei)framebuffer->width || 130 if (screen_info.texture.width != (GLsizei)framebuffer->get().width ||
130 screen_info.texture.height != (GLsizei)framebuffer->height || 131 screen_info.texture.height != (GLsizei)framebuffer->get().height ||
131 screen_info.texture.pixel_format != framebuffer->pixel_format) { 132 screen_info.texture.pixel_format != framebuffer->get().pixel_format) {
132 // Reallocate texture if the framebuffer size has changed. 133 // Reallocate texture if the framebuffer size has changed.
133 // This is expected to not happen very often and hence should not be a 134 // This is expected to not happen very often and hence should not be a
134 // performance problem. 135 // performance problem.
@@ -283,7 +284,8 @@ void RendererOpenGL::CreateRasterizer() {
283 if (rasterizer) { 284 if (rasterizer) {
284 return; 285 return;
285 } 286 }
286 287 // Initialize sRGB Usage
288 OpenGLState::ClearsRGBUsed();
287 rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info); 289 rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info);
288} 290}
289 291
@@ -356,13 +358,20 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
356 358
357 state.texture_units[0].texture = screen_info.display_texture; 359 state.texture_units[0].texture = screen_info.display_texture;
358 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; 360 state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
361 // Workaround brigthness problems in SMO by enabling sRGB in the final output
362 // if it has been used in the frame
363 // Needed because of this bug in QT
364 // QTBUG-50987
365 state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
359 state.Apply(); 366 state.Apply();
360
361 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); 367 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
362 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 368 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
363 369 // restore default state
370 state.framebuffer_srgb.enabled = false;
364 state.texture_units[0].texture = 0; 371 state.texture_units[0].texture = 0;
365 state.Apply(); 372 state.Apply();
373 // Clear sRGB state for the next frame
374 OpenGLState::ClearsRGBUsed();
366} 375}
367 376
368/** 377/**
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 961467a62..c0868c0e4 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -51,7 +51,8 @@ public:
51 ~RendererOpenGL() override; 51 ~RendererOpenGL() override;
52 52
53 /// Swap buffers (render frame) 53 /// Swap buffers (render frame)
54 void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override; 54 void SwapBuffers(
55 std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override;
55 56
56 /// Initialize the renderer 57 /// Initialize the renderer
57 bool Init() override; 58 bool Init() override;
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp
new file mode 100644
index 000000000..d84634cb3
--- /dev/null
+++ b/src/video_core/renderer_opengl/utils.cpp
@@ -0,0 +1,38 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6#include <fmt/format.h>
7#include <glad/glad.h>
8#include "common/common_types.h"
9#include "video_core/renderer_opengl/utils.h"
10
11namespace OpenGL {
12
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) {
14 if (!GLAD_GL_KHR_debug) {
15 return; // We don't need to throw an error as this is just for debugging
16 }
17 const std::string nice_addr = fmt::format("0x{:016x}", addr);
18 std::string object_label;
19
20 if (extra_info.empty()) {
21 switch (identifier) {
22 case GL_TEXTURE:
23 object_label = "Texture@" + nice_addr;
24 break;
25 case GL_PROGRAM:
26 object_label = "Shader@" + nice_addr;
27 break;
28 default:
29 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
30 break;
31 }
32 } else {
33 object_label = extra_info + '@' + nice_addr;
34 }
35 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
36}
37
38} // namespace OpenGL \ No newline at end of file
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h
new file mode 100644
index 000000000..1fcb6fc11
--- /dev/null
+++ b/src/video_core/renderer_opengl/utils.h
@@ -0,0 +1,15 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <glad/glad.h>
9#include "common/common_types.h"
10
11namespace OpenGL {
12
13void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = "");
14
15} // namespace OpenGL \ No newline at end of file
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
new file mode 100644
index 000000000..e284a4604
--- /dev/null
+++ b/src/video_core/surface.cpp
@@ -0,0 +1,483 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/common_types.h"
6#include "common/math_util.h"
7#include "video_core/surface.h"
8
9namespace VideoCore::Surface {
10
11SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
12 switch (texture_type) {
13 case Tegra::Texture::TextureType::Texture1D:
14 return SurfaceTarget::Texture1D;
15 case Tegra::Texture::TextureType::Texture2D:
16 case Tegra::Texture::TextureType::Texture2DNoMipmap:
17 return SurfaceTarget::Texture2D;
18 case Tegra::Texture::TextureType::Texture3D:
19 return SurfaceTarget::Texture3D;
20 case Tegra::Texture::TextureType::TextureCubemap:
21 return SurfaceTarget::TextureCubemap;
22 case Tegra::Texture::TextureType::Texture1DArray:
23 return SurfaceTarget::Texture1DArray;
24 case Tegra::Texture::TextureType::Texture2DArray:
25 return SurfaceTarget::Texture2DArray;
26 default:
27 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
28 UNREACHABLE();
29 return SurfaceTarget::Texture2D;
30 }
31}
32
33bool SurfaceTargetIsLayered(SurfaceTarget target) {
34 switch (target) {
35 case SurfaceTarget::Texture1D:
36 case SurfaceTarget::Texture2D:
37 case SurfaceTarget::Texture3D:
38 return false;
39 case SurfaceTarget::Texture1DArray:
40 case SurfaceTarget::Texture2DArray:
41 case SurfaceTarget::TextureCubemap:
42 return true;
43 default:
44 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
45 UNREACHABLE();
46 return false;
47 }
48}
49
50PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) {
51 switch (format) {
52 case Tegra::DepthFormat::S8_Z24_UNORM:
53 return PixelFormat::S8Z24;
54 case Tegra::DepthFormat::Z24_S8_UNORM:
55 return PixelFormat::Z24S8;
56 case Tegra::DepthFormat::Z32_FLOAT:
57 return PixelFormat::Z32F;
58 case Tegra::DepthFormat::Z16_UNORM:
59 return PixelFormat::Z16;
60 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
61 return PixelFormat::Z32FS8;
62 default:
63 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
64 UNREACHABLE();
65 }
66}
67
68PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
69 switch (format) {
70 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
71 // gamma.
72 case Tegra::RenderTargetFormat::RGBA8_SRGB:
73 return PixelFormat::RGBA8_SRGB;
74 case Tegra::RenderTargetFormat::RGBA8_UNORM:
75 return PixelFormat::ABGR8U;
76 case Tegra::RenderTargetFormat::RGBA8_SNORM:
77 return PixelFormat::ABGR8S;
78 case Tegra::RenderTargetFormat::RGBA8_UINT:
79 return PixelFormat::ABGR8UI;
80 case Tegra::RenderTargetFormat::BGRA8_SRGB:
81 return PixelFormat::BGRA8_SRGB;
82 case Tegra::RenderTargetFormat::BGRA8_UNORM:
83 return PixelFormat::BGRA8;
84 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
85 return PixelFormat::A2B10G10R10U;
86 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
87 return PixelFormat::RGBA16F;
88 case Tegra::RenderTargetFormat::RGBA16_UNORM:
89 return PixelFormat::RGBA16U;
90 case Tegra::RenderTargetFormat::RGBA16_UINT:
91 return PixelFormat::RGBA16UI;
92 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
93 return PixelFormat::RGBA32F;
94 case Tegra::RenderTargetFormat::RG32_FLOAT:
95 return PixelFormat::RG32F;
96 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
97 return PixelFormat::R11FG11FB10F;
98 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
99 return PixelFormat::B5G6R5U;
100 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
101 return PixelFormat::A1B5G5R5U;
102 case Tegra::RenderTargetFormat::RGBA32_UINT:
103 return PixelFormat::RGBA32UI;
104 case Tegra::RenderTargetFormat::R8_UNORM:
105 return PixelFormat::R8U;
106 case Tegra::RenderTargetFormat::R8_UINT:
107 return PixelFormat::R8UI;
108 case Tegra::RenderTargetFormat::RG16_FLOAT:
109 return PixelFormat::RG16F;
110 case Tegra::RenderTargetFormat::RG16_UINT:
111 return PixelFormat::RG16UI;
112 case Tegra::RenderTargetFormat::RG16_SINT:
113 return PixelFormat::RG16I;
114 case Tegra::RenderTargetFormat::RG16_UNORM:
115 return PixelFormat::RG16;
116 case Tegra::RenderTargetFormat::RG16_SNORM:
117 return PixelFormat::RG16S;
118 case Tegra::RenderTargetFormat::RG8_UNORM:
119 return PixelFormat::RG8U;
120 case Tegra::RenderTargetFormat::RG8_SNORM:
121 return PixelFormat::RG8S;
122 case Tegra::RenderTargetFormat::R16_FLOAT:
123 return PixelFormat::R16F;
124 case Tegra::RenderTargetFormat::R16_UNORM:
125 return PixelFormat::R16U;
126 case Tegra::RenderTargetFormat::R16_SNORM:
127 return PixelFormat::R16S;
128 case Tegra::RenderTargetFormat::R16_UINT:
129 return PixelFormat::R16UI;
130 case Tegra::RenderTargetFormat::R16_SINT:
131 return PixelFormat::R16I;
132 case Tegra::RenderTargetFormat::R32_FLOAT:
133 return PixelFormat::R32F;
134 case Tegra::RenderTargetFormat::R32_UINT:
135 return PixelFormat::R32UI;
136 case Tegra::RenderTargetFormat::RG32_UINT:
137 return PixelFormat::RG32UI;
138 default:
139 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
140 UNREACHABLE();
141 }
142}
143
144PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
145 Tegra::Texture::ComponentType component_type,
146 bool is_srgb) {
147 // TODO(Subv): Properly implement this
148 switch (format) {
149 case Tegra::Texture::TextureFormat::A8R8G8B8:
150 if (is_srgb) {
151 return PixelFormat::RGBA8_SRGB;
152 }
153 switch (component_type) {
154 case Tegra::Texture::ComponentType::UNORM:
155 return PixelFormat::ABGR8U;
156 case Tegra::Texture::ComponentType::SNORM:
157 return PixelFormat::ABGR8S;
158 case Tegra::Texture::ComponentType::UINT:
159 return PixelFormat::ABGR8UI;
160 }
161 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
162 UNREACHABLE();
163 case Tegra::Texture::TextureFormat::B5G6R5:
164 switch (component_type) {
165 case Tegra::Texture::ComponentType::UNORM:
166 return PixelFormat::B5G6R5U;
167 }
168 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
169 UNREACHABLE();
170 case Tegra::Texture::TextureFormat::A2B10G10R10:
171 switch (component_type) {
172 case Tegra::Texture::ComponentType::UNORM:
173 return PixelFormat::A2B10G10R10U;
174 }
175 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
176 UNREACHABLE();
177 case Tegra::Texture::TextureFormat::A1B5G5R5:
178 switch (component_type) {
179 case Tegra::Texture::ComponentType::UNORM:
180 return PixelFormat::A1B5G5R5U;
181 }
182 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
183 UNREACHABLE();
184 case Tegra::Texture::TextureFormat::R8:
185 switch (component_type) {
186 case Tegra::Texture::ComponentType::UNORM:
187 return PixelFormat::R8U;
188 case Tegra::Texture::ComponentType::UINT:
189 return PixelFormat::R8UI;
190 }
191 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
192 UNREACHABLE();
193 case Tegra::Texture::TextureFormat::G8R8:
194 switch (component_type) {
195 case Tegra::Texture::ComponentType::UNORM:
196 return PixelFormat::G8R8U;
197 case Tegra::Texture::ComponentType::SNORM:
198 return PixelFormat::G8R8S;
199 }
200 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
201 UNREACHABLE();
202 case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
203 switch (component_type) {
204 case Tegra::Texture::ComponentType::UNORM:
205 return PixelFormat::RGBA16U;
206 case Tegra::Texture::ComponentType::FLOAT:
207 return PixelFormat::RGBA16F;
208 }
209 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
210 UNREACHABLE();
211 case Tegra::Texture::TextureFormat::BF10GF11RF11:
212 switch (component_type) {
213 case Tegra::Texture::ComponentType::FLOAT:
214 return PixelFormat::R11FG11FB10F;
215 }
216 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
217 UNREACHABLE();
218 case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
219 switch (component_type) {
220 case Tegra::Texture::ComponentType::FLOAT:
221 return PixelFormat::RGBA32F;
222 case Tegra::Texture::ComponentType::UINT:
223 return PixelFormat::RGBA32UI;
224 }
225 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
226 UNREACHABLE();
227 case Tegra::Texture::TextureFormat::R32_G32:
228 switch (component_type) {
229 case Tegra::Texture::ComponentType::FLOAT:
230 return PixelFormat::RG32F;
231 case Tegra::Texture::ComponentType::UINT:
232 return PixelFormat::RG32UI;
233 }
234 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
235 UNREACHABLE();
236 case Tegra::Texture::TextureFormat::R32_G32_B32:
237 switch (component_type) {
238 case Tegra::Texture::ComponentType::FLOAT:
239 return PixelFormat::RGB32F;
240 }
241 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
242 UNREACHABLE();
243 case Tegra::Texture::TextureFormat::R16:
244 switch (component_type) {
245 case Tegra::Texture::ComponentType::FLOAT:
246 return PixelFormat::R16F;
247 case Tegra::Texture::ComponentType::UNORM:
248 return PixelFormat::R16U;
249 case Tegra::Texture::ComponentType::SNORM:
250 return PixelFormat::R16S;
251 case Tegra::Texture::ComponentType::UINT:
252 return PixelFormat::R16UI;
253 case Tegra::Texture::ComponentType::SINT:
254 return PixelFormat::R16I;
255 }
256 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
257 UNREACHABLE();
258 case Tegra::Texture::TextureFormat::R32:
259 switch (component_type) {
260 case Tegra::Texture::ComponentType::FLOAT:
261 return PixelFormat::R32F;
262 case Tegra::Texture::ComponentType::UINT:
263 return PixelFormat::R32UI;
264 }
265 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
266 UNREACHABLE();
267 case Tegra::Texture::TextureFormat::ZF32:
268 return PixelFormat::Z32F;
269 case Tegra::Texture::TextureFormat::Z16:
270 return PixelFormat::Z16;
271 case Tegra::Texture::TextureFormat::Z24S8:
272 return PixelFormat::Z24S8;
273 case Tegra::Texture::TextureFormat::DXT1:
274 return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1;
275 case Tegra::Texture::TextureFormat::DXT23:
276 return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23;
277 case Tegra::Texture::TextureFormat::DXT45:
278 return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45;
279 case Tegra::Texture::TextureFormat::DXN1:
280 return PixelFormat::DXN1;
281 case Tegra::Texture::TextureFormat::DXN2:
282 switch (component_type) {
283 case Tegra::Texture::ComponentType::UNORM:
284 return PixelFormat::DXN2UNORM;
285 case Tegra::Texture::ComponentType::SNORM:
286 return PixelFormat::DXN2SNORM;
287 }
288 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
289 UNREACHABLE();
290 case Tegra::Texture::TextureFormat::BC7U:
291 return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U;
292 case Tegra::Texture::TextureFormat::BC6H_UF16:
293 return PixelFormat::BC6H_UF16;
294 case Tegra::Texture::TextureFormat::BC6H_SF16:
295 return PixelFormat::BC6H_SF16;
296 case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
297 return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4;
298 case Tegra::Texture::TextureFormat::ASTC_2D_5X4:
299 return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4;
300 case Tegra::Texture::TextureFormat::ASTC_2D_5X5:
301 return is_srgb ? PixelFormat::ASTC_2D_5X5_SRGB : PixelFormat::ASTC_2D_5X5;
302 case Tegra::Texture::TextureFormat::ASTC_2D_8X8:
303 return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8;
304 case Tegra::Texture::TextureFormat::ASTC_2D_8X5:
305 return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5;
306 case Tegra::Texture::TextureFormat::R16_G16:
307 switch (component_type) {
308 case Tegra::Texture::ComponentType::FLOAT:
309 return PixelFormat::RG16F;
310 case Tegra::Texture::ComponentType::UNORM:
311 return PixelFormat::RG16;
312 case Tegra::Texture::ComponentType::SNORM:
313 return PixelFormat::RG16S;
314 case Tegra::Texture::ComponentType::UINT:
315 return PixelFormat::RG16UI;
316 case Tegra::Texture::ComponentType::SINT:
317 return PixelFormat::RG16I;
318 }
319 LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type));
320 UNREACHABLE();
321 default:
322 LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format),
323 static_cast<u32>(component_type));
324 UNREACHABLE();
325 }
326}
327
328ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
329 // TODO(Subv): Implement more component types
330 switch (type) {
331 case Tegra::Texture::ComponentType::UNORM:
332 return ComponentType::UNorm;
333 case Tegra::Texture::ComponentType::FLOAT:
334 return ComponentType::Float;
335 case Tegra::Texture::ComponentType::SNORM:
336 return ComponentType::SNorm;
337 case Tegra::Texture::ComponentType::UINT:
338 return ComponentType::UInt;
339 case Tegra::Texture::ComponentType::SINT:
340 return ComponentType::SInt;
341 default:
342 LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type));
343 UNREACHABLE();
344 }
345}
346
347ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
348 // TODO(Subv): Implement more render targets
349 switch (format) {
350 case Tegra::RenderTargetFormat::RGBA8_UNORM:
351 case Tegra::RenderTargetFormat::RGBA8_SRGB:
352 case Tegra::RenderTargetFormat::BGRA8_UNORM:
353 case Tegra::RenderTargetFormat::BGRA8_SRGB:
354 case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
355 case Tegra::RenderTargetFormat::R8_UNORM:
356 case Tegra::RenderTargetFormat::RG16_UNORM:
357 case Tegra::RenderTargetFormat::R16_UNORM:
358 case Tegra::RenderTargetFormat::B5G6R5_UNORM:
359 case Tegra::RenderTargetFormat::BGR5A1_UNORM:
360 case Tegra::RenderTargetFormat::RG8_UNORM:
361 case Tegra::RenderTargetFormat::RGBA16_UNORM:
362 return ComponentType::UNorm;
363 case Tegra::RenderTargetFormat::RGBA8_SNORM:
364 case Tegra::RenderTargetFormat::RG16_SNORM:
365 case Tegra::RenderTargetFormat::R16_SNORM:
366 case Tegra::RenderTargetFormat::RG8_SNORM:
367 return ComponentType::SNorm;
368 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
369 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
370 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
371 case Tegra::RenderTargetFormat::RG32_FLOAT:
372 case Tegra::RenderTargetFormat::RG16_FLOAT:
373 case Tegra::RenderTargetFormat::R16_FLOAT:
374 case Tegra::RenderTargetFormat::R32_FLOAT:
375 return ComponentType::Float;
376 case Tegra::RenderTargetFormat::RGBA32_UINT:
377 case Tegra::RenderTargetFormat::RGBA16_UINT:
378 case Tegra::RenderTargetFormat::RG16_UINT:
379 case Tegra::RenderTargetFormat::R8_UINT:
380 case Tegra::RenderTargetFormat::R16_UINT:
381 case Tegra::RenderTargetFormat::RG32_UINT:
382 case Tegra::RenderTargetFormat::R32_UINT:
383 case Tegra::RenderTargetFormat::RGBA8_UINT:
384 return ComponentType::UInt;
385 case Tegra::RenderTargetFormat::RG16_SINT:
386 case Tegra::RenderTargetFormat::R16_SINT:
387 return ComponentType::SInt;
388 default:
389 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
390 UNREACHABLE();
391 }
392}
393
394PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
395 switch (format) {
396 case Tegra::FramebufferConfig::PixelFormat::ABGR8:
397 return PixelFormat::ABGR8U;
398 default:
399 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
400 UNREACHABLE();
401 }
402}
403
404ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) {
405 switch (format) {
406 case Tegra::DepthFormat::Z16_UNORM:
407 case Tegra::DepthFormat::S8_Z24_UNORM:
408 case Tegra::DepthFormat::Z24_S8_UNORM:
409 return ComponentType::UNorm;
410 case Tegra::DepthFormat::Z32_FLOAT:
411 case Tegra::DepthFormat::Z32_S8_X24_FLOAT:
412 return ComponentType::Float;
413 default:
414 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
415 UNREACHABLE();
416 }
417}
418
419SurfaceType GetFormatType(PixelFormat pixel_format) {
420 if (static_cast<std::size_t>(pixel_format) <
421 static_cast<std::size_t>(PixelFormat::MaxColorFormat)) {
422 return SurfaceType::ColorTexture;
423 }
424
425 if (static_cast<std::size_t>(pixel_format) <
426 static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) {
427 return SurfaceType::Depth;
428 }
429
430 if (static_cast<std::size_t>(pixel_format) <
431 static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) {
432 return SurfaceType::DepthStencil;
433 }
434
435 // TODO(Subv): Implement the other formats
436 ASSERT(false);
437
438 return SurfaceType::Invalid;
439}
440
441bool IsPixelFormatASTC(PixelFormat format) {
442 switch (format) {
443 case PixelFormat::ASTC_2D_4X4:
444 case PixelFormat::ASTC_2D_5X4:
445 case PixelFormat::ASTC_2D_5X5:
446 case PixelFormat::ASTC_2D_8X8:
447 case PixelFormat::ASTC_2D_8X5:
448 case PixelFormat::ASTC_2D_4X4_SRGB:
449 case PixelFormat::ASTC_2D_5X4_SRGB:
450 case PixelFormat::ASTC_2D_5X5_SRGB:
451 case PixelFormat::ASTC_2D_8X8_SRGB:
452 case PixelFormat::ASTC_2D_8X5_SRGB:
453 return true;
454 default:
455 return false;
456 }
457}
458
459std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) {
460 return {GetDefaultBlockWidth(format), GetDefaultBlockHeight(format)};
461}
462
463bool IsFormatBCn(PixelFormat format) {
464 switch (format) {
465 case PixelFormat::DXT1:
466 case PixelFormat::DXT23:
467 case PixelFormat::DXT45:
468 case PixelFormat::DXN1:
469 case PixelFormat::DXN2SNORM:
470 case PixelFormat::DXN2UNORM:
471 case PixelFormat::BC7U:
472 case PixelFormat::BC6H_UF16:
473 case PixelFormat::BC6H_SF16:
474 case PixelFormat::DXT1_SRGB:
475 case PixelFormat::DXT23_SRGB:
476 case PixelFormat::DXT45_SRGB:
477 case PixelFormat::BC7U_SRGB:
478 return true;
479 }
480 return false;
481}
482
483} // namespace VideoCore::Surface
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
new file mode 100644
index 000000000..0ef7849a4
--- /dev/null
+++ b/src/video_core/surface.h
@@ -0,0 +1,466 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <climits>
8#include <utility>
9#include "common/assert.h"
10#include "common/common_types.h"
11#include "common/logging/log.h"
12#include "video_core/gpu.h"
13#include "video_core/textures/texture.h"
14
15namespace VideoCore::Surface {
16
17enum class PixelFormat {
18 ABGR8U = 0,
19 ABGR8S = 1,
20 ABGR8UI = 2,
21 B5G6R5U = 3,
22 A2B10G10R10U = 4,
23 A1B5G5R5U = 5,
24 R8U = 6,
25 R8UI = 7,
26 RGBA16F = 8,
27 RGBA16U = 9,
28 RGBA16UI = 10,
29 R11FG11FB10F = 11,
30 RGBA32UI = 12,
31 DXT1 = 13,
32 DXT23 = 14,
33 DXT45 = 15,
34 DXN1 = 16, // This is also known as BC4
35 DXN2UNORM = 17,
36 DXN2SNORM = 18,
37 BC7U = 19,
38 BC6H_UF16 = 20,
39 BC6H_SF16 = 21,
40 ASTC_2D_4X4 = 22,
41 G8R8U = 23,
42 G8R8S = 24,
43 BGRA8 = 25,
44 RGBA32F = 26,
45 RG32F = 27,
46 R32F = 28,
47 R16F = 29,
48 R16U = 30,
49 R16S = 31,
50 R16UI = 32,
51 R16I = 33,
52 RG16 = 34,
53 RG16F = 35,
54 RG16UI = 36,
55 RG16I = 37,
56 RG16S = 38,
57 RGB32F = 39,
58 RGBA8_SRGB = 40,
59 RG8U = 41,
60 RG8S = 42,
61 RG32UI = 43,
62 R32UI = 44,
63 ASTC_2D_8X8 = 45,
64 ASTC_2D_8X5 = 46,
65 ASTC_2D_5X4 = 47,
66 BGRA8_SRGB = 48,
67 DXT1_SRGB = 49,
68 DXT23_SRGB = 50,
69 DXT45_SRGB = 51,
70 BC7U_SRGB = 52,
71 ASTC_2D_4X4_SRGB = 53,
72 ASTC_2D_8X8_SRGB = 54,
73 ASTC_2D_8X5_SRGB = 55,
74 ASTC_2D_5X4_SRGB = 56,
75 ASTC_2D_5X5 = 57,
76 ASTC_2D_5X5_SRGB = 58,
77
78 MaxColorFormat,
79
80 // Depth formats
81 Z32F = 59,
82 Z16 = 60,
83
84 MaxDepthFormat,
85
86 // DepthStencil formats
87 Z24S8 = 61,
88 S8Z24 = 62,
89 Z32FS8 = 63,
90
91 MaxDepthStencilFormat,
92
93 Max = MaxDepthStencilFormat,
94 Invalid = 255,
95};
96
97static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);
98
99enum class ComponentType {
100 Invalid = 0,
101 SNorm = 1,
102 UNorm = 2,
103 SInt = 3,
104 UInt = 4,
105 Float = 5,
106};
107
108enum class SurfaceType {
109 ColorTexture = 0,
110 Depth = 1,
111 DepthStencil = 2,
112 Fill = 3,
113 Invalid = 4,
114};
115
116enum class SurfaceTarget {
117 Texture1D,
118 Texture2D,
119 Texture3D,
120 Texture1DArray,
121 Texture2DArray,
122 TextureCubemap,
123};
124
125/**
126 * Gets the compression factor for the specified PixelFormat. This applies to just the
127 * "compressed width" and "compressed height", not the overall compression factor of a
128 * compressed image. This is used for maintaining proper surface sizes for compressed
129 * texture formats.
130 */
131static constexpr u32 GetCompressionFactor(PixelFormat format) {
132 if (format == PixelFormat::Invalid)
133 return 0;
134
135 constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
136 1, // ABGR8U
137 1, // ABGR8S
138 1, // ABGR8UI
139 1, // B5G6R5U
140 1, // A2B10G10R10U
141 1, // A1B5G5R5U
142 1, // R8U
143 1, // R8UI
144 1, // RGBA16F
145 1, // RGBA16U
146 1, // RGBA16UI
147 1, // R11FG11FB10F
148 1, // RGBA32UI
149 4, // DXT1
150 4, // DXT23
151 4, // DXT45
152 4, // DXN1
153 4, // DXN2UNORM
154 4, // DXN2SNORM
155 4, // BC7U
156 4, // BC6H_UF16
157 4, // BC6H_SF16
158 4, // ASTC_2D_4X4
159 1, // G8R8U
160 1, // G8R8S
161 1, // BGRA8
162 1, // RGBA32F
163 1, // RG32F
164 1, // R32F
165 1, // R16F
166 1, // R16U
167 1, // R16S
168 1, // R16UI
169 1, // R16I
170 1, // RG16
171 1, // RG16F
172 1, // RG16UI
173 1, // RG16I
174 1, // RG16S
175 1, // RGB32F
176 1, // RGBA8_SRGB
177 1, // RG8U
178 1, // RG8S
179 1, // RG32UI
180 1, // R32UI
181 4, // ASTC_2D_8X8
182 4, // ASTC_2D_8X5
183 4, // ASTC_2D_5X4
184 1, // BGRA8_SRGB
185 4, // DXT1_SRGB
186 4, // DXT23_SRGB
187 4, // DXT45_SRGB
188 4, // BC7U_SRGB
189 4, // ASTC_2D_4X4_SRGB
190 4, // ASTC_2D_8X8_SRGB
191 4, // ASTC_2D_8X5_SRGB
192 4, // ASTC_2D_5X4_SRGB
193 4, // ASTC_2D_5X5
194 4, // ASTC_2D_5X5_SRGB
195 1, // Z32F
196 1, // Z16
197 1, // Z24S8
198 1, // S8Z24
199 1, // Z32FS8
200 }};
201
202 ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
203 return compression_factor_table[static_cast<std::size_t>(format)];
204}
205
206static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
207 if (format == PixelFormat::Invalid)
208 return 0;
209 constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
210 1, // ABGR8U
211 1, // ABGR8S
212 1, // ABGR8UI
213 1, // B5G6R5U
214 1, // A2B10G10R10U
215 1, // A1B5G5R5U
216 1, // R8U
217 1, // R8UI
218 1, // RGBA16F
219 1, // RGBA16U
220 1, // RGBA16UI
221 1, // R11FG11FB10F
222 1, // RGBA32UI
223 4, // DXT1
224 4, // DXT23
225 4, // DXT45
226 4, // DXN1
227 4, // DXN2UNORM
228 4, // DXN2SNORM
229 4, // BC7U
230 4, // BC6H_UF16
231 4, // BC6H_SF16
232 4, // ASTC_2D_4X4
233 1, // G8R8U
234 1, // G8R8S
235 1, // BGRA8
236 1, // RGBA32F
237 1, // RG32F
238 1, // R32F
239 1, // R16F
240 1, // R16U
241 1, // R16S
242 1, // R16UI
243 1, // R16I
244 1, // RG16
245 1, // RG16F
246 1, // RG16UI
247 1, // RG16I
248 1, // RG16S
249 1, // RGB32F
250 1, // RGBA8_SRGB
251 1, // RG8U
252 1, // RG8S
253 1, // RG32UI
254 1, // R32UI
255 8, // ASTC_2D_8X8
256 8, // ASTC_2D_8X5
257 5, // ASTC_2D_5X4
258 1, // BGRA8_SRGB
259 4, // DXT1_SRGB
260 4, // DXT23_SRGB
261 4, // DXT45_SRGB
262 4, // BC7U_SRGB
263 4, // ASTC_2D_4X4_SRGB
264 8, // ASTC_2D_8X8_SRGB
265 8, // ASTC_2D_8X5_SRGB
266 5, // ASTC_2D_5X4_SRGB
267 5, // ASTC_2D_5X5
268 5, // ASTC_2D_5X5_SRGB
269 1, // Z32F
270 1, // Z16
271 1, // Z24S8
272 1, // S8Z24
273 1, // Z32FS8
274 }};
275 ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
276 return block_width_table[static_cast<std::size_t>(format)];
277}
278
279static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
280 if (format == PixelFormat::Invalid)
281 return 0;
282
283 constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
284 1, // ABGR8U
285 1, // ABGR8S
286 1, // ABGR8UI
287 1, // B5G6R5U
288 1, // A2B10G10R10U
289 1, // A1B5G5R5U
290 1, // R8U
291 1, // R8UI
292 1, // RGBA16F
293 1, // RGBA16U
294 1, // RGBA16UI
295 1, // R11FG11FB10F
296 1, // RGBA32UI
297 4, // DXT1
298 4, // DXT23
299 4, // DXT45
300 4, // DXN1
301 4, // DXN2UNORM
302 4, // DXN2SNORM
303 4, // BC7U
304 4, // BC6H_UF16
305 4, // BC6H_SF16
306 4, // ASTC_2D_4X4
307 1, // G8R8U
308 1, // G8R8S
309 1, // BGRA8
310 1, // RGBA32F
311 1, // RG32F
312 1, // R32F
313 1, // R16F
314 1, // R16U
315 1, // R16S
316 1, // R16UI
317 1, // R16I
318 1, // RG16
319 1, // RG16F
320 1, // RG16UI
321 1, // RG16I
322 1, // RG16S
323 1, // RGB32F
324 1, // RGBA8_SRGB
325 1, // RG8U
326 1, // RG8S
327 1, // RG32UI
328 1, // R32UI
329 8, // ASTC_2D_8X8
330 5, // ASTC_2D_8X5
331 4, // ASTC_2D_5X4
332 1, // BGRA8_SRGB
333 4, // DXT1_SRGB
334 4, // DXT23_SRGB
335 4, // DXT45_SRGB
336 4, // BC7U_SRGB
337 4, // ASTC_2D_4X4_SRGB
338 8, // ASTC_2D_8X8_SRGB
339 5, // ASTC_2D_8X5_SRGB
340 4, // ASTC_2D_5X4_SRGB
341 5, // ASTC_2D_5X5
342 5, // ASTC_2D_5X5_SRGB
343 1, // Z32F
344 1, // Z16
345 1, // Z24S8
346 1, // S8Z24
347 1, // Z32FS8
348 }};
349
350 ASSERT(static_cast<std::size_t>(format) < block_height_table.size());
351 return block_height_table[static_cast<std::size_t>(format)];
352}
353
354static constexpr u32 GetFormatBpp(PixelFormat format) {
355 if (format == PixelFormat::Invalid)
356 return 0;
357
358 constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
359 32, // ABGR8U
360 32, // ABGR8S
361 32, // ABGR8UI
362 16, // B5G6R5U
363 32, // A2B10G10R10U
364 16, // A1B5G5R5U
365 8, // R8U
366 8, // R8UI
367 64, // RGBA16F
368 64, // RGBA16U
369 64, // RGBA16UI
370 32, // R11FG11FB10F
371 128, // RGBA32UI
372 64, // DXT1
373 128, // DXT23
374 128, // DXT45
375 64, // DXN1
376 128, // DXN2UNORM
377 128, // DXN2SNORM
378 128, // BC7U
379 128, // BC6H_UF16
380 128, // BC6H_SF16
381 128, // ASTC_2D_4X4
382 16, // G8R8U
383 16, // G8R8S
384 32, // BGRA8
385 128, // RGBA32F
386 64, // RG32F
387 32, // R32F
388 16, // R16F
389 16, // R16U
390 16, // R16S
391 16, // R16UI
392 16, // R16I
393 32, // RG16
394 32, // RG16F
395 32, // RG16UI
396 32, // RG16I
397 32, // RG16S
398 96, // RGB32F
399 32, // RGBA8_SRGB
400 16, // RG8U
401 16, // RG8S
402 64, // RG32UI
403 32, // R32UI
404 128, // ASTC_2D_8X8
405 128, // ASTC_2D_8X5
406 128, // ASTC_2D_5X4
407 32, // BGRA8_SRGB
408 64, // DXT1_SRGB
409 128, // DXT23_SRGB
410 128, // DXT45_SRGB
411 128, // BC7U
412 128, // ASTC_2D_4X4_SRGB
413 128, // ASTC_2D_8X8_SRGB
414 128, // ASTC_2D_8X5_SRGB
415 128, // ASTC_2D_5X4_SRGB
416 128, // ASTC_2D_5X5
417 128, // ASTC_2D_5X5_SRGB
418 32, // Z32F
419 16, // Z16
420 32, // Z24S8
421 32, // S8Z24
422 64, // Z32FS8
423 }};
424
425 ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
426 return bpp_table[static_cast<std::size_t>(format)];
427}
428
429/// Returns the sizer in bytes of the specified pixel format
430static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
431 if (pixel_format == PixelFormat::Invalid) {
432 return 0;
433 }
434 return GetFormatBpp(pixel_format) / CHAR_BIT;
435}
436
437SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);
438
439bool SurfaceTargetIsLayered(SurfaceTarget target);
440
441PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);
442
443PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);
444
445PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
446 Tegra::Texture::ComponentType component_type,
447 bool is_srgb);
448
449ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type);
450
451ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format);
452
453PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format);
454
455ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format);
456
457SurfaceType GetFormatType(PixelFormat pixel_format);
458
459bool IsPixelFormatASTC(PixelFormat format);
460
461std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);
462
463/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
464bool IsFormatBCn(PixelFormat format);
465
466} // namespace VideoCore::Surface
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index b1feacae9..bc50a4876 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1598,27 +1598,29 @@ static void DecompressBlock(uint8_t inBuf[16], const uint32_t blockWidth,
1598namespace Tegra::Texture::ASTC { 1598namespace Tegra::Texture::ASTC {
1599 1599
1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 1600std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
1601 uint32_t block_width, uint32_t block_height) { 1601 uint32_t depth, uint32_t block_width, uint32_t block_height) {
1602 uint32_t blockIdx = 0; 1602 uint32_t blockIdx = 0;
1603 std::vector<uint8_t> outData(height * width * 4); 1603 std::vector<uint8_t> outData(height * width * depth * 4);
1604 for (uint32_t j = 0; j < height; j += block_height) { 1604 for (uint32_t k = 0; k < depth; k++) {
1605 for (uint32_t i = 0; i < width; i += block_width) { 1605 for (uint32_t j = 0; j < height; j += block_height) {
1606 for (uint32_t i = 0; i < width; i += block_width) {
1606 1607
1607 uint8_t* blockPtr = data.data() + blockIdx * 16; 1608 uint8_t* blockPtr = data.data() + blockIdx * 16;
1608 1609
1609 // Blocks can be at most 12x12 1610 // Blocks can be at most 12x12
1610 uint32_t uncompData[144]; 1611 uint32_t uncompData[144];
1611 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData); 1612 ASTCC::DecompressBlock(blockPtr, block_width, block_height, uncompData);
1612 1613
1613 uint32_t decompWidth = std::min(block_width, width - i); 1614 uint32_t decompWidth = std::min(block_width, width - i);
1614 uint32_t decompHeight = std::min(block_height, height - j); 1615 uint32_t decompHeight = std::min(block_height, height - j);
1615 1616
1616 uint8_t* outRow = outData.data() + (j * width + i) * 4; 1617 uint8_t* outRow = outData.data() + (j * width + i) * 4;
1617 for (uint32_t jj = 0; jj < decompHeight; jj++) { 1618 for (uint32_t jj = 0; jj < decompHeight; jj++) {
1618 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); 1619 memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4);
1619 } 1620 }
1620 1621
1621 blockIdx++; 1622 blockIdx++;
1623 }
1622 } 1624 }
1623 } 1625 }
1624 1626
diff --git a/src/video_core/textures/astc.h b/src/video_core/textures/astc.h
index f0d7c0e56..d419dd025 100644
--- a/src/video_core/textures/astc.h
+++ b/src/video_core/textures/astc.h
@@ -10,6 +10,6 @@
10namespace Tegra::Texture::ASTC { 10namespace Tegra::Texture::ASTC {
11 11
12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height, 12std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
13 uint32_t block_width, uint32_t block_height); 13 uint32_t depth, uint32_t block_width, uint32_t block_height);
14 14
15} // namespace Tegra::Texture::ASTC 15} // namespace Tegra::Texture::ASTC
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 3d5476e5d..3066abf61 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -4,6 +4,7 @@
4 4
5#include <cmath> 5#include <cmath>
6#include <cstring> 6#include <cstring>
7#include "common/alignment.h"
7#include "common/assert.h" 8#include "common/assert.h"
8#include "core/memory.h" 9#include "core/memory.h"
9#include "video_core/gpu.h" 10#include "video_core/gpu.h"
@@ -39,72 +40,145 @@ struct alignas(64) SwizzleTable {
39constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); 40constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
40constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); 41constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
41 42
42static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 43/**
43 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 44 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
44 u32 block_height) { 45 * Instead of going gob by gob, we map the coordinates inside a block and manage from
46 * those. Block_Width is assumed to be 1.
47 */
48void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
49 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
50 const u32 y_end, const u32 z_end, const u32 tile_offset,
51 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
52 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
45 std::array<u8*, 2> data_ptrs; 53 std::array<u8*, 2> data_ptrs;
46 const std::size_t stride = width * bytes_per_pixel; 54 u32 z_address = tile_offset;
47 const std::size_t gobs_in_x = 64; 55 const u32 gob_size_x = 64;
48 const std::size_t gobs_in_y = 8; 56 const u32 gob_size_y = 8;
49 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 57 const u32 gob_size_z = 1;
50 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 58 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
51 for (std::size_t y = 0; y < height; ++y) { 59 for (u32 z = z_start; z < z_end; z++) {
52 const std::size_t gob_y_address = 60 u32 y_address = z_address;
53 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 61 u32 pixel_base = layer_z * z + y_start * stride_x;
54 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 62 for (u32 y = y_start; y < y_end; y++) {
55 const auto& table = legacy_swizzle_table[y % gobs_in_y]; 63 const auto& table = legacy_swizzle_table[y % gob_size_y];
56 for (std::size_t x = 0; x < width; ++x) { 64 for (u32 x = x_start; x < x_end; x++) {
57 const std::size_t gob_address = 65 const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]};
58 gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; 66 const u32 pixel_index{x * out_bytes_per_pixel + pixel_base};
59 const std::size_t x2 = x * bytes_per_pixel; 67 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
60 const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; 68 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
61 const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; 69 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
62 70 }
63 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 71 pixel_base += stride_x;
64 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 72 if ((y + 1) % gob_size_y == 0)
65 73 y_address += gob_size;
66 std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
67 } 74 }
75 z_address += xy_block_size;
68 } 76 }
69} 77}
70 78
71static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 79/**
72 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, 80 * This function manages ALL the GOBs(Group of Bytes) Inside a single block.
73 u32 block_height) { 81 * Instead of going gob by gob, we map the coordinates inside a block and manage from
82 * those. Block_Width is assumed to be 1.
83 */
84void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle,
85 const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end,
86 const u32 y_end, const u32 z_end, const u32 tile_offset,
87 const u32 xy_block_size, const u32 layer_z, const u32 stride_x,
88 const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {
74 std::array<u8*, 2> data_ptrs; 89 std::array<u8*, 2> data_ptrs;
75 const std::size_t stride{width * bytes_per_pixel}; 90 u32 z_address = tile_offset;
76 const std::size_t gobs_in_x = 64; 91 const u32 x_startb = x_start * bytes_per_pixel;
77 const std::size_t gobs_in_y = 8; 92 const u32 x_endb = x_end * bytes_per_pixel;
78 const std::size_t gobs_size = gobs_in_x * gobs_in_y; 93 const u32 copy_size = 16;
79 const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; 94 const u32 gob_size_x = 64;
80 const std::size_t copy_size{16}; 95 const u32 gob_size_y = 8;
81 for (std::size_t y = 0; y < height; ++y) { 96 const u32 gob_size_z = 1;
82 const std::size_t initial_gob = 97 const u32 gob_size = gob_size_x * gob_size_y * gob_size_z;
83 (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + 98 for (u32 z = z_start; z < z_end; z++) {
84 (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; 99 u32 y_address = z_address;
85 const std::size_t pixel_base{y * width * out_bytes_per_pixel}; 100 u32 pixel_base = layer_z * z + y_start * stride_x;
86 const auto& table = fast_swizzle_table[y % gobs_in_y]; 101 for (u32 y = y_start; y < y_end; y++) {
87 for (std::size_t xb = 0; xb < stride; xb += copy_size) { 102 const auto& table = fast_swizzle_table[y % gob_size_y];
88 const std::size_t gob_address{initial_gob + 103 for (u32 xb = x_startb; xb < x_endb; xb += copy_size) {
89 (xb / gobs_in_x) * gobs_size * block_height}; 104 const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]};
90 const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; 105 const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
91 const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; 106 const u32 pixel_index{out_x + pixel_base};
92 const std::size_t pixel_index{out_x + pixel_base}; 107 data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
93 data_ptrs[unswizzle] = swizzled_data + swizzle_offset; 108 data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
94 data_ptrs[!unswizzle] = unswizzled_data + pixel_index; 109 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
95 std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); 110 }
111 pixel_base += stride_x;
112 if ((y + 1) % gob_size_y == 0)
113 y_address += gob_size;
114 }
115 z_address += xy_block_size;
116 }
117}
118
119/**
120 * This function unswizzles or swizzles a texture by mapping Linear to BlockLinear Textue.
121 * The body of this function takes care of splitting the swizzled texture into blocks,
122 * and managing the extents of it. Once all the parameters of a single block are obtained,
123 * the function calls 'ProcessBlock' to process that particular Block.
124 *
125 * Documentation for the memory layout and decoding can be found at:
126 * https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces
127 */
128template <bool fast>
129void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width,
130 const u32 height, const u32 depth, const u32 bytes_per_pixel,
131 const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) {
132 auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
133 const u32 stride_x = width * out_bytes_per_pixel;
134 const u32 layer_z = height * stride_x;
135 const u32 gob_x_bytes = 64;
136 const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel;
137 const u32 gob_elements_y = 8;
138 const u32 gob_elements_z = 1;
139 const u32 block_x_elements = gob_elements_x;
140 const u32 block_y_elements = gob_elements_y * block_height;
141 const u32 block_z_elements = gob_elements_z * block_depth;
142 const u32 blocks_on_x = div_ceil(width, block_x_elements);
143 const u32 blocks_on_y = div_ceil(height, block_y_elements);
144 const u32 blocks_on_z = div_ceil(depth, block_z_elements);
145 const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z;
146 const u32 xy_block_size = gob_size * block_height;
147 const u32 block_size = xy_block_size * block_depth;
148 u32 tile_offset = 0;
149 for (u32 zb = 0; zb < blocks_on_z; zb++) {
150 const u32 z_start = zb * block_z_elements;
151 const u32 z_end = std::min(depth, z_start + block_z_elements);
152 for (u32 yb = 0; yb < blocks_on_y; yb++) {
153 const u32 y_start = yb * block_y_elements;
154 const u32 y_end = std::min(height, y_start + block_y_elements);
155 for (u32 xb = 0; xb < blocks_on_x; xb++) {
156 const u32 x_start = xb * block_x_elements;
157 const u32 x_end = std::min(width, x_start + block_x_elements);
158 if (fast) {
159 FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
160 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
161 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
162 } else {
163 PreciseProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start,
164 z_start, x_end, y_end, z_end, tile_offset, xy_block_size,
165 layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel);
166 }
167 tile_offset += block_size;
168 }
96 } 169 }
97 } 170 }
98} 171}
99 172
100void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 173void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
101 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { 174 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
175 bool unswizzle, u32 block_height, u32 block_depth) {
102 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { 176 if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
103 FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 177 SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
104 unswizzled_data, unswizzle, block_height); 178 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
105 } else { 179 } else {
106 LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, 180 SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth,
107 unswizzled_data, unswizzle, block_height); 181 bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);
108 } 182 }
109} 183}
110 184
@@ -125,7 +199,9 @@ u32 BytesPerPixel(TextureFormat format) {
125 case TextureFormat::R32_G32_B32: 199 case TextureFormat::R32_G32_B32:
126 return 12; 200 return 12;
127 case TextureFormat::ASTC_2D_4X4: 201 case TextureFormat::ASTC_2D_4X4:
202 case TextureFormat::ASTC_2D_5X4:
128 case TextureFormat::ASTC_2D_8X8: 203 case TextureFormat::ASTC_2D_8X8:
204 case TextureFormat::ASTC_2D_8X5:
129 case TextureFormat::A8R8G8B8: 205 case TextureFormat::A8R8G8B8:
130 case TextureFormat::A2B10G10R10: 206 case TextureFormat::A2B10G10R10:
131 case TextureFormat::BF10GF11RF11: 207 case TextureFormat::BF10GF11RF11:
@@ -151,14 +227,57 @@ u32 BytesPerPixel(TextureFormat format) {
151 } 227 }
152} 228}
153 229
154std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 230std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
155 u32 height, u32 block_height) { 231 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
156 std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); 232 u32 block_height, u32 block_depth) {
157 CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, 233 std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel);
158 Memory::GetPointer(address), unswizzled_data.data(), true, block_height); 234 CopySwizzledData((width + tile_size_x - 1) / tile_size_x,
235 (height + tile_size_y - 1) / tile_size_y, depth, bytes_per_pixel,
236 bytes_per_pixel, Memory::GetPointer(address), unswizzled_data.data(), true,
237 block_height, block_depth);
159 return unswizzled_data; 238 return unswizzled_data;
160} 239}
161 240
241void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
242 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
243 u32 block_height) {
244 const u32 image_width_in_gobs{(swizzled_width * bytes_per_pixel + 63) / 64};
245 for (u32 line = 0; line < subrect_height; ++line) {
246 const u32 gob_address_y =
247 (line / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
248 (line % (8 * block_height) / 8) * 512;
249 const auto& table = legacy_swizzle_table[line % 8];
250 for (u32 x = 0; x < subrect_width; ++x) {
251 const u32 gob_address = gob_address_y + (x * bytes_per_pixel / 64) * 512 * block_height;
252 const u32 swizzled_offset = gob_address + table[(x * bytes_per_pixel) % 64];
253 const VAddr source_line = unswizzled_data + line * source_pitch + x * bytes_per_pixel;
254 const VAddr dest_addr = swizzled_data + swizzled_offset;
255
256 Memory::CopyBlock(dest_addr, source_line, bytes_per_pixel);
257 }
258 }
259}
260
261void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
262 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
263 u32 block_height, u32 offset_x, u32 offset_y) {
264 for (u32 line = 0; line < subrect_height; ++line) {
265 const u32 y2 = line + offset_y;
266 const u32 gob_address_y =
267 (y2 / (8 * block_height)) * 512 * block_height + (y2 % (8 * block_height) / 8) * 512;
268 const auto& table = legacy_swizzle_table[y2 % 8];
269 for (u32 x = 0; x < subrect_width; ++x) {
270 const u32 x2 = (x + offset_x) * bytes_per_pixel;
271 const u32 gob_address = gob_address_y + (x2 / 64) * 512 * block_height;
272 const u32 swizzled_offset = gob_address + table[x2 % 64];
273 const VAddr dest_line = unswizzled_data + line * dest_pitch + x * bytes_per_pixel;
274 const VAddr source_addr = swizzled_data + swizzled_offset;
275
276 Memory::CopyBlock(dest_line, source_addr, bytes_per_pixel);
277 }
278 }
279}
280
162std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 281std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
163 u32 height) { 282 u32 height) {
164 std::vector<u8> rgba_data; 283 std::vector<u8> rgba_data;
@@ -199,4 +318,19 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
199 return rgba_data; 318 return rgba_data;
200} 319}
201 320
321std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
322 u32 block_height, u32 block_depth) {
323 if (tiled) {
324 const u32 gobs_in_x = 64;
325 const u32 gobs_in_y = 8;
326 const u32 gobs_in_z = 1;
327 const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x);
328 const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height);
329 const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth);
330 return aligned_width * aligned_height * aligned_depth;
331 } else {
332 return width * height * depth * bytes_per_pixel;
333 }
334}
335
202} // namespace Tegra::Texture 336} // namespace Tegra::Texture
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h
index 1f7b731be..ba065510b 100644
--- a/src/video_core/textures/decoders.h
+++ b/src/video_core/textures/decoders.h
@@ -10,21 +10,24 @@
10 10
11namespace Tegra::Texture { 11namespace Tegra::Texture {
12 12
13/** 13// GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents
14 * Unswizzles a swizzled texture without changing its format. 14// an small rect of (64/bytes_per_pixel)X8.
15 */ 15inline std::size_t GetGOBSize() {
16std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, 16 return 512;
17 u32 height, u32 block_height = TICEntry::DefaultBlockHeight); 17}
18 18
19/** 19/**
20 * Unswizzles a swizzled depth texture without changing its format. 20 * Unswizzles a swizzled texture without changing its format.
21 */ 21 */
22std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height, 22std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size_x, u32 tile_size_y,
23 u32 block_height = TICEntry::DefaultBlockHeight); 23 u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
24 u32 block_height = TICEntry::DefaultBlockHeight,
25 u32 block_depth = TICEntry::DefaultBlockHeight);
24 26
25/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary. 27/// Copies texture data from a buffer and performs swizzling/unswizzling as necessary.
26void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, 28void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel,
27 u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height); 29 u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data,
30 bool unswizzle, u32 block_height, u32 block_depth);
28 31
29/** 32/**
30 * Decodes an unswizzled texture into a A8R8G8B8 texture. 33 * Decodes an unswizzled texture into a A8R8G8B8 texture.
@@ -32,4 +35,19 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
32std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, 35std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
33 u32 height); 36 u32 height);
34 37
38/**
39 * This function calculates the correct size of a texture depending if it's tiled or not.
40 */
41std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth,
42 u32 block_height, u32 block_depth);
43
44/// Copies an untiled subrectangle into a tiled surface.
45void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
46 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
47 u32 block_height);
48/// Copies a tiled subrectangle into a linear surface.
49void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 swizzled_width,
50 u32 bytes_per_pixel, VAddr swizzled_data, VAddr unswizzled_data,
51 u32 block_height, u32 offset_x, u32 offset_y);
52
35} // namespace Tegra::Texture 53} // namespace Tegra::Texture
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 14aea4838..d12d2ecb8 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -141,6 +141,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
141 141
142struct TICEntry { 142struct TICEntry {
143 static constexpr u32 DefaultBlockHeight = 16; 143 static constexpr u32 DefaultBlockHeight = 16;
144 static constexpr u32 DefaultBlockDepth = 1;
144 145
145 union { 146 union {
146 u32 raw; 147 u32 raw;
@@ -161,7 +162,9 @@ struct TICEntry {
161 BitField<21, 3, TICHeaderVersion> header_version; 162 BitField<21, 3, TICHeaderVersion> header_version;
162 }; 163 };
163 union { 164 union {
165 BitField<0, 3, u32> block_width;
164 BitField<3, 3, u32> block_height; 166 BitField<3, 3, u32> block_height;
167 BitField<6, 3, u32> block_depth;
165 168
166 // High 16 bits of the pitch value 169 // High 16 bits of the pitch value
167 BitField<0, 16, u32> pitch_high; 170 BitField<0, 16, u32> pitch_high;
@@ -170,6 +173,7 @@ struct TICEntry {
170 }; 173 };
171 union { 174 union {
172 BitField<0, 16, u32> width_minus_1; 175 BitField<0, 16, u32> width_minus_1;
176 BitField<22, 1, u32> srgb_conversion;
173 BitField<23, 4, TextureType> texture_type; 177 BitField<23, 4, TextureType> texture_type;
174 }; 178 };
175 union { 179 union {
@@ -202,17 +206,32 @@ struct TICEntry {
202 return depth_minus_1 + 1; 206 return depth_minus_1 + 1;
203 } 207 }
204 208
209 u32 BlockWidth() const {
210 ASSERT(IsTiled());
211 // The block height is stored in log2 format.
212 return 1 << block_width;
213 }
214
205 u32 BlockHeight() const { 215 u32 BlockHeight() const {
206 ASSERT(header_version == TICHeaderVersion::BlockLinear || 216 ASSERT(IsTiled());
207 header_version == TICHeaderVersion::BlockLinearColorKey);
208 // The block height is stored in log2 format. 217 // The block height is stored in log2 format.
209 return 1 << block_height; 218 return 1 << block_height;
210 } 219 }
211 220
221 u32 BlockDepth() const {
222 ASSERT(IsTiled());
223 // The block height is stored in log2 format.
224 return 1 << block_depth;
225 }
226
212 bool IsTiled() const { 227 bool IsTiled() const {
213 return header_version == TICHeaderVersion::BlockLinear || 228 return header_version == TICHeaderVersion::BlockLinear ||
214 header_version == TICHeaderVersion::BlockLinearColorKey; 229 header_version == TICHeaderVersion::BlockLinearColorKey;
215 } 230 }
231
232 bool IsSrgbConversionEnabled() const {
233 return srgb_conversion != 0;
234 }
216}; 235};
217static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); 236static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");
218 237
@@ -227,6 +246,17 @@ enum class WrapMode : u32 {
227 MirrorOnceClampOGL = 7, 246 MirrorOnceClampOGL = 7,
228}; 247};
229 248
249enum class DepthCompareFunc : u32 {
250 Never = 0,
251 Less = 1,
252 Equal = 2,
253 LessEqual = 3,
254 Greater = 4,
255 NotEqual = 5,
256 GreaterEqual = 6,
257 Always = 7,
258};
259
230enum class TextureFilter : u32 { 260enum class TextureFilter : u32 {
231 Nearest = 1, 261 Nearest = 1,
232 Linear = 2, 262 Linear = 2,
@@ -244,7 +274,7 @@ struct TSCEntry {
244 BitField<3, 3, WrapMode> wrap_v; 274 BitField<3, 3, WrapMode> wrap_v;
245 BitField<6, 3, WrapMode> wrap_p; 275 BitField<6, 3, WrapMode> wrap_p;
246 BitField<9, 1, u32> depth_compare_enabled; 276 BitField<9, 1, u32> depth_compare_enabled;
247 BitField<10, 3, u32> depth_compare_func; 277 BitField<10, 3, DepthCompareFunc> depth_compare_func;
248 }; 278 };
249 union { 279 union {
250 BitField<0, 2, TextureFilter> mag_filter; 280 BitField<0, 2, TextureFilter> mag_filter;
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index 681919ae3..e0a14d48f 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -161,26 +161,4 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe
161 } 161 }
162} 162}
163 163
164static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
165 std::string extra_info = "") {
166 if (!GLAD_GL_KHR_debug) {
167 return; // We don't need to throw an error as this is just for debugging
168 }
169 const std::string nice_addr = fmt::format("0x{:016x}", addr);
170 std::string object_label;
171
172 switch (identifier) {
173 case GL_TEXTURE:
174 object_label = extra_info + "@" + nice_addr;
175 break;
176 case GL_PROGRAM:
177 object_label = "ShaderProgram@" + nice_addr;
178 break;
179 default:
180 object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
181 break;
182 }
183 glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
184}
185
186} // namespace VideoCore 164} // namespace VideoCore
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 1c83e9c34..01f2d129d 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -10,7 +10,7 @@ add_library(web_service STATIC
10create_target_directory_groups(web_service) 10create_target_directory_groups(web_service)
11 11
12get_directory_property(OPENSSL_LIBS 12get_directory_property(OPENSSL_LIBS
13 DIRECTORY ${CMAKE_SOURCE_DIR}/externals/libressl 13 DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
14 DEFINITION OPENSSL_LIBS) 14 DEFINITION OPENSSL_LIBS)
15target_compile_definitions(web_service PUBLIC -DCPPHTTPLIB_OPENSSL_SUPPORT) 15target_compile_definitions(web_service PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT)
16target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser) 16target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser)
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 033ea1ea4..9156ce802 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -2,98 +2,127 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <thread> 5#include <json.hpp>
6#include "common/assert.h"
7#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 8#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 9#include "web_service/web_backend.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
13TelemetryJson::TelemetryJson(const std::string& host, const std::string& username, 13struct TelemetryJson::Impl {
14 const std::string& token) 14 Impl(std::string host, std::string username, std::string token)
15 : host(std::move(host)), username(std::move(username)), token(std::move(token)) {} 15 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
16TelemetryJson::~TelemetryJson() = default;
17 16
18template <class T> 17 nlohmann::json& TopSection() {
19void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { 18 return sections[static_cast<u8>(Telemetry::FieldType::None)];
20 sections[static_cast<u8>(type)][name] = value; 19 }
21}
22 20
23void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { 21 const nlohmann::json& TopSection() const {
24 TopSection()[name] = sections[static_cast<unsigned>(type)]; 22 return sections[static_cast<u8>(Telemetry::FieldType::None)];
25} 23 }
24
25 template <class T>
26 void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
27 sections[static_cast<u8>(type)][name] = value;
28 }
29
30 void SerializeSection(Telemetry::FieldType type, const std::string& name) {
31 TopSection()[name] = sections[static_cast<unsigned>(type)];
32 }
33
34 nlohmann::json output;
35 std::array<nlohmann::json, 7> sections;
36 std::string host;
37 std::string username;
38 std::string token;
39};
40
41TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
42 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
43TelemetryJson::~TelemetryJson() = default;
26 44
27void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { 45void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
28 Serialize(field.GetType(), field.GetName(), field.GetValue()); 46 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
29} 47}
30 48
31void TelemetryJson::Visit(const Telemetry::Field<double>& field) { 49void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
32 Serialize(field.GetType(), field.GetName(), field.GetValue()); 50 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
33} 51}
34 52
35void TelemetryJson::Visit(const Telemetry::Field<float>& field) { 53void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
36 Serialize(field.GetType(), field.GetName(), field.GetValue()); 54 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
37} 55}
38 56
39void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { 57void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
40 Serialize(field.GetType(), field.GetName(), field.GetValue()); 58 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
41} 59}
42 60
43void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { 61void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
44 Serialize(field.GetType(), field.GetName(), field.GetValue()); 62 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
45} 63}
46 64
47void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { 65void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
48 Serialize(field.GetType(), field.GetName(), field.GetValue()); 66 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
49} 67}
50 68
51void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { 69void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
52 Serialize(field.GetType(), field.GetName(), field.GetValue()); 70 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
53} 71}
54 72
55void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { 73void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
56 Serialize(field.GetType(), field.GetName(), field.GetValue()); 74 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
57} 75}
58 76
59void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { 77void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
60 Serialize(field.GetType(), field.GetName(), field.GetValue()); 78 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
61} 79}
62 80
63void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { 81void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
64 Serialize(field.GetType(), field.GetName(), field.GetValue()); 82 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
65} 83}
66 84
67void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { 85void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
68 Serialize(field.GetType(), field.GetName(), field.GetValue()); 86 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
69} 87}
70 88
71void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { 89void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
72 Serialize(field.GetType(), field.GetName(), field.GetValue()); 90 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
73} 91}
74 92
75void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { 93void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
76 Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); 94 impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
77} 95}
78 96
79void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { 97void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
80 Serialize(field.GetType(), field.GetName(), field.GetValue().count()); 98 impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
81} 99}
82 100
83void TelemetryJson::Complete() { 101void TelemetryJson::Complete() {
84 SerializeSection(Telemetry::FieldType::App, "App"); 102 impl->SerializeSection(Telemetry::FieldType::App, "App");
85 SerializeSection(Telemetry::FieldType::Session, "Session"); 103 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
86 SerializeSection(Telemetry::FieldType::Performance, "Performance"); 104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
87 SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); 105 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
88 SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 106 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
89 SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 107
90 108 auto content = impl->TopSection().dump();
91 auto content = TopSection().dump();
92 // Send the telemetry async but don't handle the errors since they were written to the log 109 // Send the telemetry async but don't handle the errors since they were written to the log
93 Common::DetachedTasks::AddTask( 110 Common::DetachedTasks::AddTask([host{impl->host}, content]() {
94 [host{this->host}, username{this->username}, token{this->token}, content]() { 111 Client{host, "", ""}.PostJson("/telemetry", content, true);
95 Client{host, username, token}.PostJson("/telemetry", content, true); 112 });
96 }); 113}
114
115bool TelemetryJson::SubmitTestcase() {
116 impl->SerializeSection(Telemetry::FieldType::App, "App");
117 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
118 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
119 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
120
121 auto content = impl->TopSection().dump();
122 Client client(impl->host, impl->username, impl->token);
123 auto value = client.PostJson("/gamedb/testcase", content, false);
124
125 return value.result_code == Common::WebResult::Code::Success;
97} 126}
98 127
99} // namespace WebService 128} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 0fe6f9a3e..dfd202829 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <chrono>
8#include <string> 8#include <string>
9#include <json.hpp>
10#include "common/telemetry.h" 9#include "common/telemetry.h"
11#include "common/web_result.h"
12 10
13namespace WebService { 11namespace WebService {
14 12
@@ -18,8 +16,8 @@ namespace WebService {
18 */ 16 */
19class TelemetryJson : public Telemetry::VisitorInterface { 17class TelemetryJson : public Telemetry::VisitorInterface {
20public: 18public:
21 TelemetryJson(const std::string& host, const std::string& username, const std::string& token); 19 TelemetryJson(std::string host, std::string username, std::string token);
22 ~TelemetryJson(); 20 ~TelemetryJson() override;
23 21
24 void Visit(const Telemetry::Field<bool>& field) override; 22 void Visit(const Telemetry::Field<bool>& field) override;
25 void Visit(const Telemetry::Field<double>& field) override; 23 void Visit(const Telemetry::Field<double>& field) override;
@@ -37,22 +35,11 @@ public:
37 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override; 35 void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
38 36
39 void Complete() override; 37 void Complete() override;
38 bool SubmitTestcase() override;
40 39
41private: 40private:
42 nlohmann::json& TopSection() { 41 struct Impl;
43 return sections[static_cast<u8>(Telemetry::FieldType::None)]; 42 std::unique_ptr<Impl> impl;
44 }
45
46 template <class T>
47 void Serialize(Telemetry::FieldType type, const std::string& name, T value);
48
49 void SerializeSection(Telemetry::FieldType type, const std::string& name);
50
51 nlohmann::json output;
52 std::array<nlohmann::json, 7> sections;
53 std::string host;
54 std::string username;
55 std::string token;
56}; 43};
57 44
58} // namespace WebService 45} // namespace WebService
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index 124aa3863..ca4b43b93 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <json.hpp> 5#include <json.hpp>
6#include "common/web_result.h"
6#include "web_service/verify_login.h" 7#include "web_service/verify_login.h"
7#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
8 9
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 787b0fbcb..b7737b615 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -3,9 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdlib> 5#include <cstdlib>
6#include <mutex>
6#include <string> 7#include <string>
7#include <thread>
8#include <LUrlParser.h> 8#include <LUrlParser.h>
9#include <httplib.h>
10#include "common/common_types.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/web_result.h" 12#include "common/web_result.h"
11#include "core/settings.h" 13#include "core/settings.h"
@@ -20,99 +22,132 @@ constexpr u32 HTTPS_PORT = 443;
20 22
21constexpr u32 TIMEOUT_SECONDS = 30; 23constexpr u32 TIMEOUT_SECONDS = 30;
22 24
23Client::JWTCache Client::jwt_cache{}; 25struct Client::Impl {
26 Impl(std::string host, std::string username, std::string token)
27 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
28 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
29 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
30 jwt = jwt_cache.jwt;
31 }
32 }
33
34 /// A generic function handles POST, GET and DELETE request together
35 Common::WebResult GenericJson(const std::string& method, const std::string& path,
36 const std::string& data, bool allow_anonymous) {
37 if (jwt.empty()) {
38 UpdateJWT();
39 }
40
41 if (jwt.empty() && !allow_anonymous) {
42 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
43 return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
44 "Credentials needed"};
45 }
46
47 auto result = GenericJson(method, path, data, jwt);
48 if (result.result_string == "401") {
49 // Try again with new JWT
50 UpdateJWT();
51 result = GenericJson(method, path, data, jwt);
52 }
24 53
25Client::Client(const std::string& host, const std::string& username, const std::string& token) 54 return result;
26 : host(host), username(username), token(token) {
27 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
28 if (username == jwt_cache.username && token == jwt_cache.token) {
29 jwt = jwt_cache.jwt;
30 } 55 }
31}
32 56
33Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 57 /**
34 const std::string& data, const std::string& jwt, 58 * A generic function with explicit authentication method specified
35 const std::string& username, const std::string& token) { 59 * JWT is used if the jwt parameter is not empty
36 if (cli == nullptr) { 60 * username + token is used if jwt is empty but username and token are not empty
37 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 61 * anonymous if all of jwt, username and token are empty
38 int port; 62 */
39 if (parsedUrl.m_Scheme == "http") { 63 Common::WebResult GenericJson(const std::string& method, const std::string& path,
40 if (!parsedUrl.GetPort(&port)) { 64 const std::string& data, const std::string& jwt = "",
41 port = HTTP_PORT; 65 const std::string& username = "", const std::string& token = "") {
42 } 66 if (cli == nullptr) {
43 cli = 67 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
44 std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS); 68 int port;
45 } else if (parsedUrl.m_Scheme == "https") { 69 if (parsedUrl.m_Scheme == "http") {
46 if (!parsedUrl.GetPort(&port)) { 70 if (!parsedUrl.GetPort(&port)) {
47 port = HTTPS_PORT; 71 port = HTTP_PORT;
72 }
73 cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port,
74 TIMEOUT_SECONDS);
75 } else if (parsedUrl.m_Scheme == "https") {
76 if (!parsedUrl.GetPort(&port)) {
77 port = HTTPS_PORT;
78 }
79 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
80 TIMEOUT_SECONDS);
81 } else {
82 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
83 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
48 } 84 }
49 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
50 TIMEOUT_SECONDS);
51 } else {
52 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
53 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
54 } 85 }
55 } 86 if (cli == nullptr) {
56 if (cli == nullptr) { 87 LOG_ERROR(WebService, "Invalid URL {}", host + path);
57 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
58 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; 89 }
59 }
60 90
61 httplib::Headers params; 91 httplib::Headers params;
62 if (!jwt.empty()) { 92 if (!jwt.empty()) {
63 params = { 93 params = {
64 {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, 94 {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
65 }; 95 };
66 } else if (!username.empty()) { 96 } else if (!username.empty()) {
67 params = { 97 params = {
68 {std::string("x-username"), username}, 98 {std::string("x-username"), username},
69 {std::string("x-token"), token}, 99 {std::string("x-token"), token},
100 };
101 }
102
103 params.emplace(std::string("api-version"),
104 std::string(API_VERSION.begin(), API_VERSION.end()));
105 if (method != "GET") {
106 params.emplace(std::string("Content-Type"), std::string("application/json"));
70 }; 107 };
71 }
72 108
73 params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end())); 109 httplib::Request request;
74 if (method != "GET") { 110 request.method = method;
75 params.emplace(std::string("Content-Type"), std::string("application/json")); 111 request.path = path;
76 }; 112 request.headers = params;
113 request.body = data;
77 114
78 httplib::Request request; 115 httplib::Response response;
79 request.method = method;
80 request.path = path;
81 request.headers = params;
82 request.body = data;
83 116
84 httplib::Response response; 117 if (!cli->send(request, response)) {
118 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
119 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"};
120 }
85 121
86 if (!cli->send(request, response)) { 122 if (response.status >= 400) {
87 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 123 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
88 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; 124 response.status);
89 } 125 return Common::WebResult{Common::WebResult::Code::HttpError,
126 std::to_string(response.status)};
127 }
90 128
91 if (response.status >= 400) { 129 auto content_type = response.headers.find("content-type");
92 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
93 response.status);
94 return Common::WebResult{Common::WebResult::Code::HttpError,
95 std::to_string(response.status)};
96 }
97 130
98 auto content_type = response.headers.find("content-type"); 131 if (content_type == response.headers.end()) {
132 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
133 return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
134 }
99 135
100 if (content_type == response.headers.end()) { 136 if (content_type->second.find("application/json") == std::string::npos &&
101 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 137 content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
102 return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
139 content_type->second);
140 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
141 }
142 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
103 } 143 }
104 144
105 if (content_type->second.find("application/json") == std::string::npos && 145 // Retrieve a new JWT from given username and token
106 content_type->second.find("text/html; charset=utf-8") == std::string::npos) { 146 void UpdateJWT() {
107 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 147 if (username.empty() || token.empty()) {
108 content_type->second); 148 return;
109 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; 149 }
110 }
111 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
112}
113 150
114void Client::UpdateJWT() {
115 if (!username.empty() && !token.empty()) {
116 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token); 151 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
117 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != Common::WebResult::Code::Success) {
118 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
@@ -123,27 +158,39 @@ void Client::UpdateJWT() {
123 jwt_cache.jwt = jwt = result.returned_data; 158 jwt_cache.jwt = jwt = result.returned_data;
124 } 159 }
125 } 160 }
126}
127 161
128Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 162 std::string host;
129 const std::string& data, bool allow_anonymous) { 163 std::string username;
130 if (jwt.empty()) { 164 std::string token;
131 UpdateJWT(); 165 std::string jwt;
132 } 166 std::unique_ptr<httplib::Client> cli;
167
168 struct JWTCache {
169 std::mutex mutex;
170 std::string username;
171 std::string token;
172 std::string jwt;
173 };
174 static inline JWTCache jwt_cache;
175};
133 176
134 if (jwt.empty() && !allow_anonymous) { 177Client::Client(std::string host, std::string username, std::string token)
135 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 178 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
136 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"};
137 }
138 179
139 auto result = GenericJson(method, path, data, jwt); 180Client::~Client() = default;
140 if (result.result_string == "401") { 181
141 // Try again with new JWT 182Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
142 UpdateJWT(); 183 bool allow_anonymous) {
143 result = GenericJson(method, path, data, jwt); 184 return impl->GenericJson("POST", path, data, allow_anonymous);
144 } 185}
186
187Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
188 return impl->GenericJson("GET", path, "", allow_anonymous);
189}
145 190
146 return result; 191Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
192 bool allow_anonymous) {
193 return impl->GenericJson("DELETE", path, data, allow_anonymous);
147} 194}
148 195
149} // namespace WebService 196} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index d75fbcc15..c637e09df 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -4,23 +4,19 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <memory>
8#include <mutex>
9#include <string> 8#include <string>
10#include <tuple>
11#include <httplib.h>
12#include "common/common_types.h"
13#include "common/web_result.h"
14 9
15namespace httplib { 10namespace Common {
16class Client; 11struct WebResult;
17} 12}
18 13
19namespace WebService { 14namespace WebService {
20 15
21class Client { 16class Client {
22public: 17public:
23 Client(const std::string& host, const std::string& username, const std::string& token); 18 Client(std::string host, std::string username, std::string token);
19 ~Client();
24 20
25 /** 21 /**
26 * Posts JSON to the specified path. 22 * Posts JSON to the specified path.
@@ -30,9 +26,7 @@ public:
30 * @return the result of the request. 26 * @return the result of the request.
31 */ 27 */
32 Common::WebResult PostJson(const std::string& path, const std::string& data, 28 Common::WebResult PostJson(const std::string& path, const std::string& data,
33 bool allow_anonymous) { 29 bool allow_anonymous);
34 return GenericJson("POST", path, data, allow_anonymous);
35 }
36 30
37 /** 31 /**
38 * Gets JSON from the specified path. 32 * Gets JSON from the specified path.
@@ -40,9 +34,7 @@ public:
40 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 34 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
41 * @return the result of the request. 35 * @return the result of the request.
42 */ 36 */
43 Common::WebResult GetJson(const std::string& path, bool allow_anonymous) { 37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
44 return GenericJson("GET", path, "", allow_anonymous);
45 }
46 38
47 /** 39 /**
48 * Deletes JSON to the specified path. 40 * Deletes JSON to the specified path.
@@ -52,41 +44,11 @@ public:
52 * @return the result of the request. 44 * @return the result of the request.
53 */ 45 */
54 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 46 Common::WebResult DeleteJson(const std::string& path, const std::string& data,
55 bool allow_anonymous) { 47 bool allow_anonymous);
56 return GenericJson("DELETE", path, data, allow_anonymous);
57 }
58 48
59private: 49private:
60 /// A generic function handles POST, GET and DELETE request together 50 struct Impl;
61 Common::WebResult GenericJson(const std::string& method, const std::string& path, 51 std::unique_ptr<Impl> impl;
62 const std::string& data, bool allow_anonymous);
63
64 /**
65 * A generic function with explicit authentication method specified
66 * JWT is used if the jwt parameter is not empty
67 * username + token is used if jwt is empty but username and token are not empty
68 * anonymous if all of jwt, username and token are empty
69 */
70 Common::WebResult GenericJson(const std::string& method, const std::string& path,
71 const std::string& data, const std::string& jwt = "",
72 const std::string& username = "", const std::string& token = "");
73
74 // Retrieve a new JWT from given username and token
75 void UpdateJWT();
76
77 std::string host;
78 std::string username;
79 std::string token;
80 std::string jwt;
81 std::unique_ptr<httplib::Client> cli;
82
83 struct JWTCache {
84 std::mutex mutex;
85 std::string username;
86 std::string token;
87 std::string jwt;
88 };
89 static JWTCache jwt_cache;
90}; 52};
91 53
92} // namespace WebService 54} // namespace WebService
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 04464ad5e..f9ca2948e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -56,6 +56,8 @@ add_executable(yuzu
56 main.h 56 main.h
57 ui_settings.cpp 57 ui_settings.cpp
58 ui_settings.h 58 ui_settings.h
59 util/limitable_input_dialog.cpp
60 util/limitable_input_dialog.h
59 util/spinbox.cpp 61 util/spinbox.cpp
60 util/spinbox.h 62 util/spinbox.h
61 util/util.cpp 63 util/util.cpp
@@ -82,10 +84,10 @@ set(UIS
82) 84)
83 85
84file(GLOB COMPAT_LIST 86file(GLOB COMPAT_LIST
85 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc 87 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
86 ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) 88 ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
87file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*) 89file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
88file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) 90file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
89 91
90qt5_wrap_ui(UI_HDRS ${UIS}) 92qt5_wrap_ui(UI_HDRS ${UIS})
91 93
@@ -121,7 +123,7 @@ target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets)
121target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 123target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
122 124
123if (YUZU_ENABLE_COMPATIBILITY_REPORTING) 125if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
124 add_definitions(-DYUZU_ENABLE_COMPATIBILITY_REPORTING) 126 target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING)
125endif() 127endif()
126 128
127if (USE_DISCORD_PRESENCE) 129if (USE_DISCORD_PRESENCE)
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 4e4c108ab..39eef8858 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -8,7 +8,6 @@
8 8
9#include "common/microprofile.h" 9#include "common/microprofile.h"
10#include "common/scm_rev.h" 10#include "common/scm_rev.h"
11#include "common/string_util.h"
12#include "core/core.h" 11#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h" 12#include "core/frontend/framebuffer_layout.h"
14#include "core/settings.h" 13#include "core/settings.h"
@@ -107,9 +106,9 @@ private:
107GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 106GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
108 : QWidget(parent), child(nullptr), emu_thread(emu_thread) { 107 : QWidget(parent), child(nullptr), emu_thread(emu_thread) {
109 108
110 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, 109 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
111 Common::g_scm_branch, Common::g_scm_desc); 110 .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
112 setWindowTitle(QString::fromStdString(window_title)); 111 setAttribute(Qt::WA_AcceptTouchEvents);
113 112
114 InputCommon::Init(); 113 InputCommon::Init();
115 InputCommon::StartJoystickEventHandler(); 114 InputCommon::StartJoystickEventHandler();
@@ -190,11 +189,17 @@ QByteArray GRenderWindow::saveGeometry() {
190 return geometry; 189 return geometry;
191} 190}
192 191
193qreal GRenderWindow::windowPixelRatio() { 192qreal GRenderWindow::windowPixelRatio() const {
194 // windowHandle() might not be accessible until the window is displayed to screen. 193 // windowHandle() might not be accessible until the window is displayed to screen.
195 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; 194 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
196} 195}
197 196
197std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const {
198 const qreal pixel_ratio = windowPixelRatio();
199 return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
200 static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
201}
202
198void GRenderWindow::closeEvent(QCloseEvent* event) { 203void GRenderWindow::closeEvent(QCloseEvent* event) {
199 emit Closed(); 204 emit Closed();
200 QWidget::closeEvent(event); 205 QWidget::closeEvent(event);
@@ -209,31 +214,81 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
209} 214}
210 215
211void GRenderWindow::mousePressEvent(QMouseEvent* event) { 216void GRenderWindow::mousePressEvent(QMouseEvent* event) {
217 if (event->source() == Qt::MouseEventSynthesizedBySystem)
218 return; // touch input is handled in TouchBeginEvent
219
212 auto pos = event->pos(); 220 auto pos = event->pos();
213 if (event->button() == Qt::LeftButton) { 221 if (event->button() == Qt::LeftButton) {
214 qreal pixelRatio = windowPixelRatio(); 222 const auto [x, y] = ScaleTouch(pos);
215 this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), 223 this->TouchPressed(x, y);
216 static_cast<unsigned>(pos.y() * pixelRatio));
217 } else if (event->button() == Qt::RightButton) { 224 } else if (event->button() == Qt::RightButton) {
218 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); 225 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
219 } 226 }
220} 227}
221 228
222void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { 229void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
230 if (event->source() == Qt::MouseEventSynthesizedBySystem)
231 return; // touch input is handled in TouchUpdateEvent
232
223 auto pos = event->pos(); 233 auto pos = event->pos();
224 qreal pixelRatio = windowPixelRatio(); 234 const auto [x, y] = ScaleTouch(pos);
225 this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), 235 this->TouchMoved(x, y);
226 std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
227 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); 236 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
228} 237}
229 238
230void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { 239void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
240 if (event->source() == Qt::MouseEventSynthesizedBySystem)
241 return; // touch input is handled in TouchEndEvent
242
231 if (event->button() == Qt::LeftButton) 243 if (event->button() == Qt::LeftButton)
232 this->TouchReleased(); 244 this->TouchReleased();
233 else if (event->button() == Qt::RightButton) 245 else if (event->button() == Qt::RightButton)
234 InputCommon::GetMotionEmu()->EndTilt(); 246 InputCommon::GetMotionEmu()->EndTilt();
235} 247}
236 248
249void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
250 // TouchBegin always has exactly one touch point, so take the .first()
251 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
252 this->TouchPressed(x, y);
253}
254
255void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
256 QPointF pos;
257 int active_points = 0;
258
259 // average all active touch points
260 for (const auto tp : event->touchPoints()) {
261 if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
262 active_points++;
263 pos += tp.pos();
264 }
265 }
266
267 pos /= active_points;
268
269 const auto [x, y] = ScaleTouch(pos);
270 this->TouchMoved(x, y);
271}
272
273void GRenderWindow::TouchEndEvent() {
274 this->TouchReleased();
275}
276
277bool GRenderWindow::event(QEvent* event) {
278 if (event->type() == QEvent::TouchBegin) {
279 TouchBeginEvent(static_cast<QTouchEvent*>(event));
280 return true;
281 } else if (event->type() == QEvent::TouchUpdate) {
282 TouchUpdateEvent(static_cast<QTouchEvent*>(event));
283 return true;
284 } else if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) {
285 TouchEndEvent();
286 return true;
287 }
288
289 return QWidget::event(event);
290}
291
237void GRenderWindow::focusOutEvent(QFocusEvent* event) { 292void GRenderWindow::focusOutEvent(QFocusEvent* event) {
238 QWidget::focusOutEvent(event); 293 QWidget::focusOutEvent(event);
239 InputCommon::GetKeyboard()->ReleaseAllKeys(); 294 InputCommon::GetKeyboard()->ReleaseAllKeys();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index f133bfadf..873985564 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -15,6 +15,7 @@
15 15
16class QKeyEvent; 16class QKeyEvent;
17class QScreen; 17class QScreen;
18class QTouchEvent;
18 19
19class GGLWidgetInternal; 20class GGLWidgetInternal;
20class GMainWindow; 21class GMainWindow;
@@ -119,7 +120,7 @@ public:
119 void restoreGeometry(const QByteArray& geometry); // overridden 120 void restoreGeometry(const QByteArray& geometry); // overridden
120 QByteArray saveGeometry(); // overridden 121 QByteArray saveGeometry(); // overridden
121 122
122 qreal windowPixelRatio(); 123 qreal windowPixelRatio() const;
123 124
124 void closeEvent(QCloseEvent* event) override; 125 void closeEvent(QCloseEvent* event) override;
125 126
@@ -130,6 +131,8 @@ public:
130 void mouseMoveEvent(QMouseEvent* event) override; 131 void mouseMoveEvent(QMouseEvent* event) override;
131 void mouseReleaseEvent(QMouseEvent* event) override; 132 void mouseReleaseEvent(QMouseEvent* event) override;
132 133
134 bool event(QEvent* event) override;
135
133 void focusOutEvent(QFocusEvent* event) override; 136 void focusOutEvent(QFocusEvent* event) override;
134 137
135 void OnClientAreaResized(unsigned width, unsigned height); 138 void OnClientAreaResized(unsigned width, unsigned height);
@@ -148,6 +151,11 @@ signals:
148 void Closed(); 151 void Closed();
149 152
150private: 153private:
154 std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const;
155 void TouchBeginEvent(const QTouchEvent* event);
156 void TouchUpdateEvent(const QTouchEvent* event);
157 void TouchEndEvent();
158
151 void OnMinimalClientAreaChangeRequest( 159 void OnMinimalClientAreaChangeRequest(
152 const std::pair<unsigned, unsigned>& minimal_size) override; 160 const std::pair<unsigned, unsigned>& minimal_size) override;
153 161
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 91e754274..5f0896f84 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -5,6 +5,7 @@
5#include <QButtonGroup> 5#include <QButtonGroup>
6#include <QMessageBox> 6#include <QMessageBox>
7#include <QPushButton> 7#include <QPushButton>
8#include <QtConcurrent/qtconcurrentrun.h>
8#include "common/logging/log.h" 9#include "common/logging/log.h"
9#include "common/telemetry.h" 10#include "common/telemetry.h"
10#include "core/core.h" 11#include "core/core.h"
@@ -23,6 +24,8 @@ CompatDB::CompatDB(QWidget* parent)
23 connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext); 24 connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
24 connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext); 25 connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
25 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit); 26 connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
27 connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
28 &CompatDB::OnTestcaseSubmitted);
26} 29}
27 30
28CompatDB::~CompatDB() = default; 31CompatDB::~CompatDB() = default;
@@ -48,18 +51,38 @@ void CompatDB::Submit() {
48 } 51 }
49 break; 52 break;
50 case CompatDBPage::Final: 53 case CompatDBPage::Final:
54 back();
51 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId()); 55 LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
52 Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility", 56 Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
53 compatibility->checkedId()); 57 compatibility->checkedId());
54 // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a 58
55 // workaround 59 button(NextButton)->setEnabled(false);
60 button(NextButton)->setText(tr("Submitting"));
56 button(QWizard::CancelButton)->setVisible(false); 61 button(QWizard::CancelButton)->setVisible(false);
62
63 testcase_watcher.setFuture(QtConcurrent::run(
64 [this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
57 break; 65 break;
58 default: 66 default:
59 LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); 67 LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
60 } 68 }
61} 69}
62 70
71void CompatDB::OnTestcaseSubmitted() {
72 if (!testcase_watcher.result()) {
73 QMessageBox::critical(this, tr("Communication error"),
74 tr("An error occured while sending the Testcase"));
75 button(NextButton)->setEnabled(true);
76 button(NextButton)->setText(tr("Next"));
77 button(QWizard::CancelButton)->setVisible(true);
78 } else {
79 next();
80 // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
81 // workaround
82 button(QWizard::CancelButton)->setVisible(false);
83 }
84}
85
63void CompatDB::EnableNext() { 86void CompatDB::EnableNext() {
64 button(NextButton)->setEnabled(true); 87 button(NextButton)->setEnabled(true);
65} 88}
diff --git a/src/yuzu/compatdb.h b/src/yuzu/compatdb.h
index ca0dd11d6..5381f67f7 100644
--- a/src/yuzu/compatdb.h
+++ b/src/yuzu/compatdb.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <QFutureWatcher>
8#include <QWizard> 9#include <QWizard>
9 10
10namespace Ui { 11namespace Ui {
@@ -19,8 +20,11 @@ public:
19 ~CompatDB(); 20 ~CompatDB();
20 21
21private: 22private:
23 QFutureWatcher<bool> testcase_watcher;
24
22 std::unique_ptr<Ui::CompatDB> ui; 25 std::unique_ptr<Ui::CompatDB> ui;
23 26
24 void Submit(); 27 void Submit();
28 void OnTestcaseSubmitted();
25 void EnableNext(); 29 void EnableNext();
26}; 30};
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 650dd03c0..d4fd60a73 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -4,6 +4,7 @@
4 4
5#include <QSettings> 5#include <QSettings>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/hle/service/acc/profile_manager.h"
7#include "input_common/main.h" 8#include "input_common/main.h"
8#include "yuzu/configuration/config.h" 9#include "yuzu/configuration/config.h"
9#include "yuzu/ui_settings.h" 10#include "yuzu/ui_settings.h"
@@ -12,11 +13,16 @@ Config::Config() {
12 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 13 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
13 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; 14 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
14 FileUtil::CreateFullPath(qt_config_loc); 15 FileUtil::CreateFullPath(qt_config_loc);
15 qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 16 qt_config =
17 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
16 18
17 Reload(); 19 Reload();
18} 20}
19 21
22Config::~Config() {
23 Save();
24}
25
20const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { 26const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
21 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, 27 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
22 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, 28 Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
@@ -85,8 +91,8 @@ void Config::ReadValues() {
85 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); 91 Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();
86 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool(); 92 Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();
87 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); 93 Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt();
88 Settings::values.use_accurate_framebuffers = 94 Settings::values.use_accurate_gpu_emulation =
89 qt_config->value("use_accurate_framebuffers", false).toBool(); 95 qt_config->value("use_accurate_gpu_emulation", false).toBool();
90 96
91 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat(); 97 Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();
92 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); 98 Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat();
@@ -122,7 +128,11 @@ void Config::ReadValues() {
122 128
123 qt_config->beginGroup("System"); 129 qt_config->beginGroup("System");
124 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 130 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
125 Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); 131 Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
132
133 Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0,
134 Service::Account::MAX_USERS - 1);
135
126 Settings::values.language_index = qt_config->value("language_index", 1).toInt(); 136 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
127 qt_config->endGroup(); 137 qt_config->endGroup();
128 138
@@ -134,6 +144,7 @@ void Config::ReadValues() {
134 qt_config->beginGroup("Debugging"); 144 qt_config->beginGroup("Debugging");
135 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); 145 Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
136 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); 146 Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
147 Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
137 qt_config->endGroup(); 148 qt_config->endGroup();
138 149
139 qt_config->beginGroup("WebService"); 150 qt_config->beginGroup("WebService");
@@ -232,7 +243,7 @@ void Config::SaveValues() {
232 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); 243 qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);
233 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit); 244 qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);
234 qt_config->setValue("frame_limit", Settings::values.frame_limit); 245 qt_config->setValue("frame_limit", Settings::values.frame_limit);
235 qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers); 246 qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);
236 247
237 // Cast to double because Qt's written float values are not human-readable 248 // Cast to double because Qt's written float values are not human-readable
238 qt_config->setValue("bg_red", (double)Settings::values.bg_red); 249 qt_config->setValue("bg_red", (double)Settings::values.bg_red);
@@ -257,7 +268,9 @@ void Config::SaveValues() {
257 268
258 qt_config->beginGroup("System"); 269 qt_config->beginGroup("System");
259 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 270 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
260 qt_config->setValue("username", QString::fromStdString(Settings::values.username)); 271 qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
272 qt_config->setValue("current_user", Settings::values.current_user);
273
261 qt_config->setValue("language_index", Settings::values.language_index); 274 qt_config->setValue("language_index", Settings::values.language_index);
262 qt_config->endGroup(); 275 qt_config->endGroup();
263 276
@@ -269,6 +282,7 @@ void Config::SaveValues() {
269 qt_config->beginGroup("Debugging"); 282 qt_config->beginGroup("Debugging");
270 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); 283 qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
271 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); 284 qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
285 qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
272 qt_config->endGroup(); 286 qt_config->endGroup();
273 287
274 qt_config->beginGroup("WebService"); 288 qt_config->beginGroup("WebService");
@@ -333,9 +347,3 @@ void Config::Reload() {
333void Config::Save() { 347void Config::Save() {
334 SaveValues(); 348 SaveValues();
335} 349}
336
337Config::~Config() {
338 Save();
339
340 delete qt_config;
341}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index cbf745ea2..9c99c1b75 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <memory>
8#include <string> 9#include <string>
9#include <QVariant> 10#include <QVariant>
10#include "core/settings.h" 11#include "core/settings.h"
@@ -12,12 +13,6 @@
12class QSettings; 13class QSettings;
13 14
14class Config { 15class Config {
15 QSettings* qt_config;
16 std::string qt_config_loc;
17
18 void ReadValues();
19 void SaveValues();
20
21public: 16public:
22 Config(); 17 Config();
23 ~Config(); 18 ~Config();
@@ -27,4 +22,11 @@ public:
27 22
28 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 23 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
29 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; 24 static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
25
26private:
27 void ReadValues();
28 void SaveValues();
29
30 std::unique_ptr<QSettings> qt_config;
31 std::string qt_config_loc;
30}; 32};
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 45d84f19a..9e765fc93 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -33,6 +33,7 @@ void ConfigureDebug::setConfiguration() {
33 ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 33 ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn());
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
36} 37}
37 38
38void ConfigureDebug::applyConfiguration() { 39void ConfigureDebug::applyConfiguration() {
@@ -40,6 +41,7 @@ void ConfigureDebug::applyConfiguration() {
40 Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); 41 Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
41 UISettings::values.show_console = ui->toggle_console->isChecked(); 42 UISettings::values.show_console = ui->toggle_console->isChecked();
42 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 43 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
44 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
43 Debugger::ToggleConsole(); 45 Debugger::ToggleConsole();
44 Log::Filter filter; 46 Log::Filter filter;
45 filter.ParseFilterString(Settings::values.log_filter); 47 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 5ae7276bd..ff4987604 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -107,6 +107,29 @@
107 </widget> 107 </widget>
108 </item> 108 </item>
109 <item> 109 <item>
110 <widget class="QGroupBox" name="groupBox_3">
111 <property name="title">
112 <string>Homebrew</string>
113 </property>
114 <layout class="QVBoxLayout" name="verticalLayout">
115 <item>
116 <layout class="QHBoxLayout" name="horizontalLayout">
117 <item>
118 <widget class="QLabel" name="label">
119 <property name="text">
120 <string>Arguments String</string>
121 </property>
122 </widget>
123 </item>
124 <item>
125 <widget class="QLineEdit" name="homebrew_args_edit"/>
126 </item>
127 </layout>
128 </item>
129 </layout>
130 </widget>
131 </item>
132 <item>
110 <spacer name="verticalSpacer"> 133 <spacer name="verticalSpacer">
111 <property name="orientation"> 134 <property name="orientation">
112 <enum>Qt::Vertical</enum> 135 <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index f5db9e55b..537d6e576 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -31,6 +31,7 @@ void ConfigureGeneral::setConfiguration() {
31 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 31 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
32 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit); 32 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
33 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 33 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
34 ui->enable_nfc->setChecked(Settings::values.enable_nfc);
34} 35}
35 36
36void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { 37void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
@@ -45,4 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
45 46
46 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 47 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
47 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 48 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
49 Settings::values.enable_nfc = ui->enable_nfc->isChecked();
48} 50}
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 1775c4d40..b82fffde8 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -68,19 +68,26 @@
68 <property name="title"> 68 <property name="title">
69 <string>Emulation</string> 69 <string>Emulation</string>
70 </property> 70 </property>
71 <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> 71 <layout class="QHBoxLayout" name="EmulationHorizontalLayout">
72 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout">
74 <item>
75 <widget class="QCheckBox" name="use_docked_mode">
76 <property name="text">
77 <string>Enable docked mode</string>
78 </property>
79 </widget>
80 </item>
72 <item> 81 <item>
73 <layout class="QVBoxLayout" name="EmulationVerticalLayout"> 82 <widget class="QCheckBox" name="enable_nfc">
74 <item> 83 <property name="text">
75 <widget class="QCheckBox" name="use_docked_mode"> 84 <string>Enable NFC</string>
76 <property name="text"> 85 </property>
77 <string>Enable docked mode</string> 86 </widget>
78 </property>
79 </widget>
80 </item>
81 </layout>
82 </item> 87 </item>
83 </layout> 88 </layout>
89 </item>
90 </layout>
84 </widget> 91 </widget>
85 </item> 92 </item>
86 <item> 93 <item>
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index cd1549462..8290b4384 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -75,7 +75,7 @@ void ConfigureGraphics::setConfiguration() {
75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); 75 static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); 76 ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
77 ui->frame_limit->setValue(Settings::values.frame_limit); 77 ui->frame_limit->setValue(Settings::values.frame_limit);
78 ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); 78 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 79 bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
80 Settings::values.bg_blue); 80 Settings::values.bg_blue);
81 ui->bg_button->setStyleSheet( 81 ui->bg_button->setStyleSheet(
@@ -87,7 +87,7 @@ void ConfigureGraphics::applyConfiguration() {
87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 87 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); 88 Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
89 Settings::values.frame_limit = ui->frame_limit->value(); 89 Settings::values.frame_limit = ui->frame_limit->value();
90 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); 90 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
91 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 91 Settings::values.bg_red = static_cast<float>(bg_color.redF());
92 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 92 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); 93 Settings::values.bg_blue = static_cast<float>(bg_color.blueF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 8fc00af1b..91fcad994 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -50,9 +50,9 @@
50 </layout> 50 </layout>
51 </item> 51 </item>
52 <item> 52 <item>
53 <widget class="QCheckBox" name="use_accurate_framebuffers"> 53 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
54 <property name="text"> 54 <property name="text">
55 <string>Use accurate framebuffers (slow)</string> 55 <string>Use accurate GPU emulation (slow)</string>
56 </property> 56 </property>
57 </widget> 57 </widget>
58 </item> 58 </item>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 473937ea9..42a7beac6 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -5,6 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <memory> 6#include <memory>
7#include <utility> 7#include <utility>
8#include <QMenu>
8#include <QMessageBox> 9#include <QMessageBox>
9#include <QTimer> 10#include <QTimer>
10#include "common/param_package.h" 11#include "common/param_package.h"
@@ -128,28 +129,63 @@ ConfigureInput::ConfigureInput(QWidget* parent)
128 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; 129 analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
129 130
130 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { 131 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
131 if (button_map[button_id]) 132 if (!button_map[button_id])
132 connect(button_map[button_id], &QPushButton::released, [=]() { 133 continue;
133 handleClick( 134 button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu);
134 button_map[button_id], 135 connect(button_map[button_id], &QPushButton::released, [=]() {
135 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, 136 handleClick(
136 InputCommon::Polling::DeviceType::Button); 137 button_map[button_id],
137 }); 138 [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; },
139 InputCommon::Polling::DeviceType::Button);
140 });
141 connect(button_map[button_id], &QPushButton::customContextMenuRequested,
142 [=](const QPoint& menu_location) {
143 QMenu context_menu;
144 context_menu.addAction(tr("Clear"), [&] {
145 buttons_param[button_id].Clear();
146 button_map[button_id]->setText(tr("[not set]"));
147 });
148 context_menu.addAction(tr("Restore Default"), [&] {
149 buttons_param[button_id] = Common::ParamPackage{
150 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
151 button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
152 });
153 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
154 });
138 } 155 }
139 156
140 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { 157 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
141 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { 158 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
142 if (analog_map_buttons[analog_id][sub_button_id] != nullptr) { 159 if (!analog_map_buttons[analog_id][sub_button_id])
143 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, 160 continue;
144 [=]() { 161 analog_map_buttons[analog_id][sub_button_id]->setContextMenuPolicy(
145 handleClick(analog_map_buttons[analog_id][sub_button_id], 162 Qt::CustomContextMenu);
146 [=](const Common::ParamPackage& params) { 163 connect(analog_map_buttons[analog_id][sub_button_id], &QPushButton::released, [=]() {
147 SetAnalogButton(params, analogs_param[analog_id], 164 handleClick(analog_map_buttons[analog_id][sub_button_id],
148 analog_sub_buttons[sub_button_id]); 165 [=](const Common::ParamPackage& params) {
149 }, 166 SetAnalogButton(params, analogs_param[analog_id],
150 InputCommon::Polling::DeviceType::Button); 167 analog_sub_buttons[sub_button_id]);
168 },
169 InputCommon::Polling::DeviceType::Button);
170 });
171 connect(analog_map_buttons[analog_id][sub_button_id],
172 &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) {
173 QMenu context_menu;
174 context_menu.addAction(tr("Clear"), [&] {
175 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
176 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
151 }); 177 });
152 } 178 context_menu.addAction(tr("Restore Default"), [&] {
179 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
180 Config::default_analogs[analog_id][sub_button_id])};
181 SetAnalogButton(params, analogs_param[analog_id],
182 analog_sub_buttons[sub_button_id]);
183 analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
184 analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
185 });
186 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
187 menu_location));
188 });
153 } 189 }
154 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { 190 connect(analog_map_stick[analog_id], &QPushButton::released, [=]() {
155 QMessageBox::information(this, tr("Information"), 191 QMessageBox::information(this, tr("Information"),
@@ -162,6 +198,7 @@ ConfigureInput::ConfigureInput(QWidget* parent)
162 }); 198 });
163 } 199 }
164 200
201 connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); });
165 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); 202 connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); });
166 203
167 timeout_timer->setSingleShot(true); 204 timeout_timer->setSingleShot(true);
@@ -215,7 +252,21 @@ void ConfigureInput::restoreDefaults() {
215 } 252 }
216 } 253 }
217 updateButtonLabels(); 254 updateButtonLabels();
218 applyConfiguration(); 255}
256
257void ConfigureInput::ClearAll() {
258 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
259 if (button_map[button_id] && button_map[button_id]->isEnabled())
260 buttons_param[button_id].Clear();
261 }
262 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
263 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
264 if (analog_map_buttons[analog_id][sub_button_id] &&
265 analog_map_buttons[analog_id][sub_button_id]->isEnabled())
266 analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
267 }
268 }
269 updateButtonLabels();
219} 270}
220 271
221void ConfigureInput::updateButtonLabels() { 272void ConfigureInput::updateButtonLabels() {
@@ -271,7 +322,7 @@ void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool a
271 } 322 }
272 323
273 updateButtonLabels(); 324 updateButtonLabels();
274 input_setter = boost::none; 325 input_setter = {};
275} 326}
276 327
277void ConfigureInput::keyPressEvent(QKeyEvent* event) { 328void ConfigureInput::keyPressEvent(QKeyEvent* event) {
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index a0bef86d5..32c7183f9 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,11 +7,13 @@
7#include <array> 7#include <array>
8#include <functional> 8#include <functional>
9#include <memory> 9#include <memory>
10#include <optional>
10#include <string> 11#include <string>
11#include <unordered_map> 12#include <unordered_map>
13
12#include <QKeyEvent> 14#include <QKeyEvent>
13#include <QWidget> 15#include <QWidget>
14#include <boost/optional.hpp> 16
15#include "common/param_package.h" 17#include "common/param_package.h"
16#include "core/settings.h" 18#include "core/settings.h"
17#include "input_common/main.h" 19#include "input_common/main.h"
@@ -41,7 +43,7 @@ private:
41 std::unique_ptr<QTimer> poll_timer; 43 std::unique_ptr<QTimer> poll_timer;
42 44
43 /// This will be the the setting function when an input is awaiting configuration. 45 /// This will be the the setting function when an input is awaiting configuration.
44 boost::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 46 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
45 47
46 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 48 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
47 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 49 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
@@ -72,6 +74,9 @@ private:
72 void loadConfiguration(); 74 void loadConfiguration();
73 /// Restore all buttons to their default values. 75 /// Restore all buttons to their default values.
74 void restoreDefaults(); 76 void restoreDefaults();
77 /// Clear all input configuration
78 void ClearAll();
79
75 /// Update UI to reflect current configuration. 80 /// Update UI to reflect current configuration.
76 void updateButtonLabels(); 81 void updateButtonLabels();
77 82
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index 8bfa5df62..8a019a693 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -695,6 +695,34 @@ Capture:</string>
695 </spacer> 695 </spacer>
696 </item> 696 </item>
697 <item> 697 <item>
698 <widget class="QPushButton" name="buttonClearAll">
699 <property name="sizePolicy">
700 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
701 <horstretch>0</horstretch>
702 <verstretch>0</verstretch>
703 </sizepolicy>
704 </property>
705 <property name="sizeIncrement">
706 <size>
707 <width>0</width>
708 <height>0</height>
709 </size>
710 </property>
711 <property name="baseSize">
712 <size>
713 <width>0</width>
714 <height>0</height>
715 </size>
716 </property>
717 <property name="layoutDirection">
718 <enum>Qt::LeftToRight</enum>
719 </property>
720 <property name="text">
721 <string>Clear All</string>
722 </property>
723 </widget>
724 </item>
725 <item>
698 <widget class="QPushButton" name="buttonRestoreDefaults"> 726 <widget class="QPushButton" name="buttonRestoreDefaults">
699 <property name="sizePolicy"> 727 <property name="sizePolicy">
700 <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> 728 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index e9ed9c38f..b4b4a4a56 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -2,14 +2,27 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <QFileDialog>
7#include <QGraphicsItem>
8#include <QGraphicsScene>
9#include <QHeaderView>
5#include <QMessageBox> 10#include <QMessageBox>
11#include <QStandardItemModel>
12#include <QTreeView>
13#include <QVBoxLayout>
14#include "common/assert.h"
15#include "common/file_util.h"
16#include "common/string_util.h"
6#include "core/core.h" 17#include "core/core.h"
18#include "core/hle/service/acc/profile_manager.h"
7#include "core/settings.h" 19#include "core/settings.h"
8#include "ui_configure_system.h" 20#include "ui_configure_system.h"
9#include "yuzu/configuration/configure_system.h" 21#include "yuzu/configuration/configure_system.h"
10#include "yuzu/main.h" 22#include "yuzu/util/limitable_input_dialog.h"
11 23
12static const std::array<int, 12> days_in_month = {{ 24namespace {
25constexpr std::array<int, 12> days_in_month = {{
13 31, 26 31,
14 29, 27 29,
15 31, 28 31,
@@ -24,13 +37,108 @@ static const std::array<int, 12> days_in_month = {{
24 31, 37 31,
25}}; 38}};
26 39
27ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { 40// Same backup JPEG used by acc IProfile::GetImage if no jpeg found
41constexpr std::array<u8, 107> backup_jpeg{
42 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
43 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
44 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
45 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
46 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
47 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
48 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
49};
50
51QString GetImagePath(Service::Account::UUID uuid) {
52 const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
53 "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
54 return QString::fromStdString(path);
55}
56
57QString GetAccountUsername(const Service::Account::ProfileManager& manager,
58 Service::Account::UUID uuid) {
59 Service::Account::ProfileBase profile;
60 if (!manager.GetProfileBase(uuid, profile)) {
61 return {};
62 }
63
64 const auto text = Common::StringFromFixedZeroTerminatedBuffer(
65 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
66 return QString::fromStdString(text);
67}
68
69QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) {
70 return ConfigureSystem::tr("%1\n%2",
71 "%1 is the profile username, %2 is the formatted UUID (e.g. "
72 "00112233-4455-6677-8899-AABBCCDDEEFF))")
73 .arg(username, QString::fromStdString(uuid.FormatSwitch()));
74}
75
76QPixmap GetIcon(Service::Account::UUID uuid) {
77 QPixmap icon{GetImagePath(uuid)};
78
79 if (!icon) {
80 icon.fill(Qt::black);
81 icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size()));
82 }
83
84 return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
85}
86
87QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) {
88 return LimitableInputDialog::GetText(parent, ConfigureSystem::tr("Enter Username"),
89 description_text, 1,
90 static_cast<int>(Service::Account::profile_username_size));
91}
92} // Anonymous namespace
93
94ConfigureSystem::ConfigureSystem(QWidget* parent)
95 : QWidget(parent), ui(new Ui::ConfigureSystem),
96 profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
28 ui->setupUi(this); 97 ui->setupUi(this);
29 connect(ui->combo_birthmonth, 98 connect(ui->combo_birthmonth,
30 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 99 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
31 &ConfigureSystem::updateBirthdayComboBox); 100 &ConfigureSystem::UpdateBirthdayComboBox);
32 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, 101 connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,
33 &ConfigureSystem::refreshConsoleID); 102 &ConfigureSystem::RefreshConsoleID);
103
104 layout = new QVBoxLayout;
105 tree_view = new QTreeView;
106 item_model = new QStandardItemModel(tree_view);
107 tree_view->setModel(item_model);
108
109 tree_view->setAlternatingRowColors(true);
110 tree_view->setSelectionMode(QHeaderView::SingleSelection);
111 tree_view->setSelectionBehavior(QHeaderView::SelectRows);
112 tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
113 tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
114 tree_view->setSortingEnabled(true);
115 tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
116 tree_view->setUniformRowHeights(true);
117 tree_view->setIconSize({64, 64});
118 tree_view->setContextMenuPolicy(Qt::NoContextMenu);
119
120 item_model->insertColumns(0, 1);
121 item_model->setHeaderData(0, Qt::Horizontal, "Users");
122
123 // We must register all custom types with the Qt Automoc system so that we are able to use it
124 // with signals/slots. In this case, QList falls under the umbrells of custom types.
125 qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
126
127 layout->setContentsMargins(0, 0, 0, 0);
128 layout->setSpacing(0);
129 layout->addWidget(tree_view);
130
131 ui->scrollArea->setLayout(layout);
132
133 connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser);
134
135 connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser);
136 connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser);
137 connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser);
138 connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage);
139
140 scene = new QGraphicsScene;
141 ui->current_user_icon->setScene(scene);
34 142
35 this->setConfiguration(); 143 this->setConfiguration();
36} 144}
@@ -39,8 +147,45 @@ ConfigureSystem::~ConfigureSystem() = default;
39 147
40void ConfigureSystem::setConfiguration() { 148void ConfigureSystem::setConfiguration() {
41 enabled = !Core::System::GetInstance().IsPoweredOn(); 149 enabled = !Core::System::GetInstance().IsPoweredOn();
42 ui->edit_username->setText(QString::fromStdString(Settings::values.username)); 150
43 ui->combo_language->setCurrentIndex(Settings::values.language_index); 151 ui->combo_language->setCurrentIndex(Settings::values.language_index);
152
153 item_model->removeRows(0, item_model->rowCount());
154 list_items.clear();
155
156 PopulateUserList();
157 UpdateCurrentUser();
158}
159
160void ConfigureSystem::PopulateUserList() {
161 const auto& profiles = profile_manager->GetAllUsers();
162 for (const auto& user : profiles) {
163 Service::Account::ProfileBase profile;
164 if (!profile_manager->GetProfileBase(user, profile))
165 continue;
166
167 const auto username = Common::StringFromFixedZeroTerminatedBuffer(
168 reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
169
170 list_items.push_back(QList<QStandardItem*>{new QStandardItem{
171 GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}});
172 }
173
174 for (const auto& item : list_items)
175 item_model->appendRow(item);
176}
177
178void ConfigureSystem::UpdateCurrentUser() {
179 ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS);
180
181 const auto& current_user = profile_manager->GetUser(Settings::values.current_user);
182 ASSERT(current_user);
183 const auto username = GetAccountUsername(*profile_manager, *current_user);
184
185 scene->clear();
186 scene->addPixmap(
187 GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
188 ui->current_user_username->setText(username);
44} 189}
45 190
46void ConfigureSystem::ReadSystemSettings() {} 191void ConfigureSystem::ReadSystemSettings() {}
@@ -48,12 +193,12 @@ void ConfigureSystem::ReadSystemSettings() {}
48void ConfigureSystem::applyConfiguration() { 193void ConfigureSystem::applyConfiguration() {
49 if (!enabled) 194 if (!enabled)
50 return; 195 return;
51 Settings::values.username = ui->edit_username->text().toStdString(); 196
52 Settings::values.language_index = ui->combo_language->currentIndex(); 197 Settings::values.language_index = ui->combo_language->currentIndex();
53 Settings::Apply(); 198 Settings::Apply();
54} 199}
55 200
56void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { 201void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) {
57 if (birthmonth_index < 0 || birthmonth_index >= 12) 202 if (birthmonth_index < 0 || birthmonth_index >= 12)
58 return; 203 return;
59 204
@@ -78,7 +223,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) {
78 ui->combo_birthday->setCurrentIndex(birthday_index); 223 ui->combo_birthday->setCurrentIndex(birthday_index);
79} 224}
80 225
81void ConfigureSystem::refreshConsoleID() { 226void ConfigureSystem::RefreshConsoleID() {
82 QMessageBox::StandardButton reply; 227 QMessageBox::StandardButton reply;
83 QString warning_text = tr("This will replace your current virtual Switch with a new one. " 228 QString warning_text = tr("This will replace your current virtual Switch with a new one. "
84 "Your current virtual Switch will not be recoverable. " 229 "Your current virtual Switch will not be recoverable. "
@@ -92,3 +237,130 @@ void ConfigureSystem::refreshConsoleID() {
92 ui->label_console_id->setText( 237 ui->label_console_id->setText(
93 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); 238 tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));
94} 239}
240
241void ConfigureSystem::SelectUser(const QModelIndex& index) {
242 Settings::values.current_user =
243 std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1);
244
245 UpdateCurrentUser();
246
247 ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2);
248 ui->pm_rename->setEnabled(true);
249 ui->pm_set_image->setEnabled(true);
250}
251
252void ConfigureSystem::AddUser() {
253 const auto username =
254 GetProfileUsernameFromUser(this, tr("Enter a username for the new user:"));
255 if (username.isEmpty()) {
256 return;
257 }
258
259 const auto uuid = Service::Account::UUID::Generate();
260 profile_manager->CreateNewUser(uuid, username.toStdString());
261
262 item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
263}
264
265void ConfigureSystem::RenameUser() {
266 const auto user = tree_view->currentIndex().row();
267 const auto uuid = profile_manager->GetUser(user);
268 ASSERT(uuid);
269
270 Service::Account::ProfileBase profile;
271 if (!profile_manager->GetProfileBase(*uuid, profile))
272 return;
273
274 const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:"));
275 if (new_username.isEmpty()) {
276 return;
277 }
278
279 const auto username_std = new_username.toStdString();
280 std::fill(profile.username.begin(), profile.username.end(), '\0');
281 std::copy(username_std.begin(), username_std.end(), profile.username.begin());
282
283 profile_manager->SetProfileBase(*uuid, profile);
284
285 item_model->setItem(
286 user, 0,
287 new QStandardItem{GetIcon(*uuid),
288 FormatUserEntryText(QString::fromStdString(username_std), *uuid)});
289 UpdateCurrentUser();
290}
291
292void ConfigureSystem::DeleteUser() {
293 const auto index = tree_view->currentIndex().row();
294 const auto uuid = profile_manager->GetUser(index);
295 ASSERT(uuid);
296 const auto username = GetAccountUsername(*profile_manager, *uuid);
297
298 const auto confirm = QMessageBox::question(
299 this, tr("Confirm Delete"),
300 tr("You are about to delete user with name \"%1\". Are you sure?").arg(username));
301
302 if (confirm == QMessageBox::No)
303 return;
304
305 if (Settings::values.current_user == tree_view->currentIndex().row())
306 Settings::values.current_user = 0;
307 UpdateCurrentUser();
308
309 if (!profile_manager->RemoveUser(*uuid))
310 return;
311
312 item_model->removeRows(tree_view->currentIndex().row(), 1);
313 tree_view->clearSelection();
314
315 ui->pm_remove->setEnabled(false);
316 ui->pm_rename->setEnabled(false);
317}
318
319void ConfigureSystem::SetUserImage() {
320 const auto index = tree_view->currentIndex().row();
321 const auto uuid = profile_manager->GetUser(index);
322 ASSERT(uuid);
323
324 const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
325 tr("JPEG Images (*.jpg *.jpeg)"));
326
327 if (file.isEmpty()) {
328 return;
329 }
330
331 const auto image_path = GetImagePath(*uuid);
332 if (QFile::exists(image_path) && !QFile::remove(image_path)) {
333 QMessageBox::warning(
334 this, tr("Error deleting image"),
335 tr("Error occurred attempting to overwrite previous image at: %1.").arg(image_path));
336 return;
337 }
338
339 const auto raw_path = QString::fromStdString(
340 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010");
341 const QFileInfo raw_info{raw_path};
342 if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) {
343 QMessageBox::warning(this, tr("Error deleting file"),
344 tr("Unable to delete existing file: %1.").arg(raw_path));
345 return;
346 }
347
348 const QString absolute_dst_path = QFileInfo{image_path}.absolutePath();
349 if (!QDir{raw_path}.mkpath(absolute_dst_path)) {
350 QMessageBox::warning(
351 this, tr("Error creating user image directory"),
352 tr("Unable to create directory %1 for storing user images.").arg(absolute_dst_path));
353 return;
354 }
355
356 if (!QFile::copy(file, image_path)) {
357 QMessageBox::warning(this, tr("Error copying user image"),
358 tr("Unable to copy image from %1 to %2").arg(file, image_path));
359 return;
360 }
361
362 const auto username = GetAccountUsername(*profile_manager, *uuid);
363 item_model->setItem(index, 0,
364 new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
365 UpdateCurrentUser();
366}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index f13de17d4..07764e1f7 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -5,8 +5,20 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8
9#include <QList>
8#include <QWidget> 10#include <QWidget>
9 11
12class QGraphicsScene;
13class QStandardItem;
14class QStandardItemModel;
15class QTreeView;
16class QVBoxLayout;
17
18namespace Service::Account {
19class ProfileManager;
20}
21
10namespace Ui { 22namespace Ui {
11class ConfigureSystem; 23class ConfigureSystem;
12} 24}
@@ -16,23 +28,39 @@ class ConfigureSystem : public QWidget {
16 28
17public: 29public:
18 explicit ConfigureSystem(QWidget* parent = nullptr); 30 explicit ConfigureSystem(QWidget* parent = nullptr);
19 ~ConfigureSystem(); 31 ~ConfigureSystem() override;
20 32
21 void applyConfiguration(); 33 void applyConfiguration();
22 void setConfiguration(); 34 void setConfiguration();
23 35
24public slots:
25 void updateBirthdayComboBox(int birthmonth_index);
26 void refreshConsoleID();
27
28private: 36private:
29 void ReadSystemSettings(); 37 void ReadSystemSettings();
30 38
39 void UpdateBirthdayComboBox(int birthmonth_index);
40 void RefreshConsoleID();
41
42 void PopulateUserList();
43 void UpdateCurrentUser();
44 void SelectUser(const QModelIndex& index);
45 void AddUser();
46 void RenameUser();
47 void DeleteUser();
48 void SetUserImage();
49
50 QVBoxLayout* layout;
51 QTreeView* tree_view;
52 QStandardItemModel* item_model;
53 QGraphicsScene* scene;
54
55 std::vector<QList<QStandardItem*>> list_items;
56
31 std::unique_ptr<Ui::ConfigureSystem> ui; 57 std::unique_ptr<Ui::ConfigureSystem> ui;
32 bool enabled; 58 bool enabled = false;
59
60 int birthmonth = 0;
61 int birthday = 0;
62 int language_index = 0;
63 int sound_index = 0;
33 64
34 std::u16string username; 65 std::unique_ptr<Service::Account::ProfileManager> profile_manager;
35 int birthmonth, birthday;
36 int language_index;
37 int sound_index;
38}; 66};
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index f3f8db038..020b32a37 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>360</width> 9 <width>360</width>
10 <height>377</height> 10 <height>483</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -22,34 +22,28 @@
22 <string>System Settings</string> 22 <string>System Settings</string>
23 </property> 23 </property>
24 <layout class="QGridLayout" name="gridLayout"> 24 <layout class="QGridLayout" name="gridLayout">
25 <item row="0" column="0"> 25 <item row="1" column="0">
26 <widget class="QLabel" name="label_username"> 26 <widget class="QLabel" name="label_language">
27 <property name="text"> 27 <property name="text">
28 <string>Username</string> 28 <string>Language</string>
29 </property> 29 </property>
30 </widget> 30 </widget>
31 </item> 31 </item>
32 <item row="0" column="1"> 32 <item row="0" column="0">
33 <widget class="QLineEdit" name="edit_username"> 33 <widget class="QLabel" name="label_birthday">
34 <property name="sizePolicy"> 34 <property name="text">
35 <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> 35 <string>Birthday</string>
36 <horstretch>0</horstretch>
37 <verstretch>0</verstretch>
38 </sizepolicy>
39 </property>
40 <property name="maxLength">
41 <number>32</number>
42 </property> 36 </property>
43 </widget> 37 </widget>
44 </item> 38 </item>
45 <item row="1" column="0"> 39 <item row="3" column="0">
46 <widget class="QLabel" name="label_birthday"> 40 <widget class="QLabel" name="label_console_id">
47 <property name="text"> 41 <property name="text">
48 <string>Birthday</string> 42 <string>Console ID:</string>
49 </property> 43 </property>
50 </widget> 44 </widget>
51 </item> 45 </item>
52 <item row="1" column="1"> 46 <item row="0" column="1">
53 <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> 47 <layout class="QHBoxLayout" name="horizontalLayout_birthday2">
54 <item> 48 <item>
55 <widget class="QComboBox" name="combo_birthmonth"> 49 <widget class="QComboBox" name="combo_birthmonth">
@@ -120,14 +114,7 @@
120 </item> 114 </item>
121 </layout> 115 </layout>
122 </item> 116 </item>
123 <item row="2" column="0"> 117 <item row="1" column="1">
124 <widget class="QLabel" name="label_language">
125 <property name="text">
126 <string>Language</string>
127 </property>
128 </widget>
129 </item>
130 <item row="2" column="1">
131 <widget class="QComboBox" name="combo_language"> 118 <widget class="QComboBox" name="combo_language">
132 <property name="toolTip"> 119 <property name="toolTip">
133 <string>Note: this can be overridden when region setting is auto-select</string> 120 <string>Note: this can be overridden when region setting is auto-select</string>
@@ -187,31 +174,31 @@
187 <string>Russian (Русский)</string> 174 <string>Russian (Русский)</string>
188 </property> 175 </property>
189 </item> 176 </item>
190 <item> 177 <item>
191 <property name="text"> 178 <property name="text">
192 <string>Taiwanese</string> 179 <string>Taiwanese</string>
193 </property> 180 </property>
194 </item> 181 </item>
195 <item> 182 <item>
196 <property name="text"> 183 <property name="text">
197 <string>British English</string> 184 <string>British English</string>
198 </property> 185 </property>
199 </item> 186 </item>
200 <item> 187 <item>
201 <property name="text"> 188 <property name="text">
202 <string>Canadian French</string> 189 <string>Canadian French</string>
203 </property> 190 </property>
204 </item> 191 </item>
205 <item> 192 <item>
206 <property name="text"> 193 <property name="text">
207 <string>Latin American Spanish</string> 194 <string>Latin American Spanish</string>
208 </property> 195 </property>
209 </item> 196 </item>
210 <item> 197 <item>
211 <property name="text"> 198 <property name="text">
212 <string>Simplified Chinese</string> 199 <string>Simplified Chinese</string>
213 </property> 200 </property>
214 </item> 201 </item>
215 <item> 202 <item>
216 <property name="text"> 203 <property name="text">
217 <string>Traditional Chinese (正體中文)</string> 204 <string>Traditional Chinese (正體中文)</string>
@@ -219,14 +206,14 @@
219 </item> 206 </item>
220 </widget> 207 </widget>
221 </item> 208 </item>
222 <item row="3" column="0"> 209 <item row="2" column="0">
223 <widget class="QLabel" name="label_sound"> 210 <widget class="QLabel" name="label_sound">
224 <property name="text"> 211 <property name="text">
225 <string>Sound output mode</string> 212 <string>Sound output mode</string>
226 </property> 213 </property>
227 </widget> 214 </widget>
228 </item> 215 </item>
229 <item row="3" column="1"> 216 <item row="2" column="1">
230 <widget class="QComboBox" name="combo_sound"> 217 <widget class="QComboBox" name="combo_sound">
231 <item> 218 <item>
232 <property name="text"> 219 <property name="text">
@@ -245,14 +232,7 @@
245 </item> 232 </item>
246 </widget> 233 </widget>
247 </item> 234 </item>
248 <item row="4" column="0"> 235 <item row="3" column="1">
249 <widget class="QLabel" name="label_console_id">
250 <property name="text">
251 <string>Console ID:</string>
252 </property>
253 </widget>
254 </item>
255 <item row="4" column="1">
256 <widget class="QPushButton" name="button_regenerate_console_id"> 236 <widget class="QPushButton" name="button_regenerate_console_id">
257 <property name="sizePolicy"> 237 <property name="sizePolicy">
258 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 238 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -272,6 +252,143 @@
272 </widget> 252 </widget>
273 </item> 253 </item>
274 <item> 254 <item>
255 <widget class="QGroupBox" name="gridGroupBox">
256 <property name="title">
257 <string>Profile Manager</string>
258 </property>
259 <layout class="QGridLayout" name="gridLayout_2">
260 <property name="sizeConstraint">
261 <enum>QLayout::SetNoConstraint</enum>
262 </property>
263 <item row="0" column="0">
264 <layout class="QHBoxLayout" name="horizontalLayout_2">
265 <item>
266 <widget class="QLabel" name="label">
267 <property name="sizePolicy">
268 <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
269 <horstretch>0</horstretch>
270 <verstretch>0</verstretch>
271 </sizepolicy>
272 </property>
273 <property name="text">
274 <string>Current User</string>
275 </property>
276 </widget>
277 </item>
278 <item>
279 <widget class="QGraphicsView" name="current_user_icon">
280 <property name="minimumSize">
281 <size>
282 <width>48</width>
283 <height>48</height>
284 </size>
285 </property>
286 <property name="maximumSize">
287 <size>
288 <width>48</width>
289 <height>48</height>
290 </size>
291 </property>
292 <property name="verticalScrollBarPolicy">
293 <enum>Qt::ScrollBarAlwaysOff</enum>
294 </property>
295 <property name="horizontalScrollBarPolicy">
296 <enum>Qt::ScrollBarAlwaysOff</enum>
297 </property>
298 <property name="interactive">
299 <bool>false</bool>
300 </property>
301 </widget>
302 </item>
303 <item>
304 <widget class="QLabel" name="current_user_username">
305 <property name="sizePolicy">
306 <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
307 <horstretch>0</horstretch>
308 <verstretch>0</verstretch>
309 </sizepolicy>
310 </property>
311 <property name="text">
312 <string>Username</string>
313 </property>
314 </widget>
315 </item>
316 </layout>
317 </item>
318 <item row="1" column="0">
319 <widget class="QScrollArea" name="scrollArea">
320 <property name="sizePolicy">
321 <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
322 <horstretch>0</horstretch>
323 <verstretch>0</verstretch>
324 </sizepolicy>
325 </property>
326 <property name="frameShape">
327 <enum>QFrame::StyledPanel</enum>
328 </property>
329 <property name="widgetResizable">
330 <bool>false</bool>
331 </property>
332 </widget>
333 </item>
334 <item row="2" column="0">
335 <layout class="QHBoxLayout" name="horizontalLayout_3">
336 <item>
337 <widget class="QPushButton" name="pm_set_image">
338 <property name="enabled">
339 <bool>false</bool>
340 </property>
341 <property name="text">
342 <string>Set Image</string>
343 </property>
344 </widget>
345 </item>
346 <item>
347 <spacer name="horizontalSpacer">
348 <property name="orientation">
349 <enum>Qt::Horizontal</enum>
350 </property>
351 <property name="sizeHint" stdset="0">
352 <size>
353 <width>40</width>
354 <height>20</height>
355 </size>
356 </property>
357 </spacer>
358 </item>
359 <item>
360 <widget class="QPushButton" name="pm_add">
361 <property name="text">
362 <string>Add</string>
363 </property>
364 </widget>
365 </item>
366 <item>
367 <widget class="QPushButton" name="pm_rename">
368 <property name="enabled">
369 <bool>false</bool>
370 </property>
371 <property name="text">
372 <string>Rename</string>
373 </property>
374 </widget>
375 </item>
376 <item>
377 <widget class="QPushButton" name="pm_remove">
378 <property name="enabled">
379 <bool>false</bool>
380 </property>
381 <property name="text">
382 <string>Remove</string>
383 </property>
384 </widget>
385 </item>
386 </layout>
387 </item>
388 </layout>
389 </widget>
390 </item>
391 <item>
275 <widget class="QLabel" name="label_disable_info"> 392 <widget class="QLabel" name="label_disable_info">
276 <property name="text"> 393 <property name="text">
277 <string>System settings are available only when game is not running.</string> 394 <string>System settings are available only when game is not running.</string>
@@ -281,19 +398,6 @@
281 </property> 398 </property>
282 </widget> 399 </widget>
283 </item> 400 </item>
284 <item>
285 <spacer name="verticalSpacer">
286 <property name="orientation">
287 <enum>Qt::Vertical</enum>
288 </property>
289 <property name="sizeHint" stdset="0">
290 <size>
291 <width>20</width>
292 <height>40</height>
293 </size>
294 </property>
295 </spacer>
296 </item>
297 </layout> 401 </layout>
298 </item> 402 </item>
299 </layout> 403 </layout>
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
index b5c88f944..67ed0ba6d 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map>
6#include <QLabel> 5#include <QLabel>
7#include <QMetaType> 6#include <QMetaType>
8#include <QPushButton> 7#include <QPushButton>
@@ -32,21 +31,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const {
32 switch (role) { 31 switch (role) {
33 case Qt::DisplayRole: { 32 case Qt::DisplayRole: {
34 if (index.column() == 0) { 33 if (index.column() == 0) {
35 static const std::map<Tegra::DebugContext::Event, QString> map = { 34 return DebugContextEventToString(event);
36 {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")},
37 {Tegra::DebugContext::Event::MaxwellCommandProcessed,
38 tr("Maxwell command processed")},
39 {Tegra::DebugContext::Event::IncomingPrimitiveBatch,
40 tr("Incoming primitive batch")},
41 {Tegra::DebugContext::Event::FinishedPrimitiveBatch,
42 tr("Finished primitive batch")},
43 };
44
45 DEBUG_ASSERT(map.size() ==
46 static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents));
47 return (map.find(event) != map.end()) ? map.at(event) : QString();
48 } 35 }
49
50 break; 36 break;
51 } 37 }
52 38
@@ -128,6 +114,23 @@ void BreakPointModel::OnResumed() {
128 active_breakpoint = context->active_breakpoint; 114 active_breakpoint = context->active_breakpoint;
129} 115}
130 116
117QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) {
118 switch (event) {
119 case Tegra::DebugContext::Event::MaxwellCommandLoaded:
120 return tr("Maxwell command loaded");
121 case Tegra::DebugContext::Event::MaxwellCommandProcessed:
122 return tr("Maxwell command processed");
123 case Tegra::DebugContext::Event::IncomingPrimitiveBatch:
124 return tr("Incoming primitive batch");
125 case Tegra::DebugContext::Event::FinishedPrimitiveBatch:
126 return tr("Finished primitive batch");
127 case Tegra::DebugContext::Event::NumEvents:
128 break;
129 }
130
131 return tr("Unknown debug context event");
132}
133
131GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( 134GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
132 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) 135 std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent)
133 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( 136 : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver(
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
index 7112b87e6..fb488e38f 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
@@ -29,6 +29,8 @@ public:
29 void OnResumed(); 29 void OnResumed();
30 30
31private: 31private:
32 static QString DebugContextEventToString(Tegra::DebugContext::Event event);
33
32 std::weak_ptr<Tegra::DebugContext> context_weak; 34 std::weak_ptr<Tegra::DebugContext> context_weak;
33 bool at_breakpoint; 35 bool at_breakpoint;
34 Tegra::DebugContext::Event active_breakpoint; 36 Tegra::DebugContext::Event active_breakpoint;
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index cbcd5dd5f..707747422 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -382,12 +382,13 @@ void GraphicsSurfaceWidget::OnUpdate() {
382 // TODO: Implement a good way to visualize alpha components! 382 // TODO: Implement a good way to visualize alpha components!
383 383
384 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); 384 QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
385 boost::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); 385 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
386 386
387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. 387 // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. 388 // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture( 389 auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
390 *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height); 390 *address, 1, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width,
391 surface_height, 1U);
391 392
392 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, 393 auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
393 surface_width, surface_height); 394 surface_width, surface_height);
@@ -443,7 +444,7 @@ void GraphicsSurfaceWidget::SaveSurface() {
443 pixmap->save(&file, "PNG"); 444 pixmap->save(&file, "PNG");
444 } else if (selectedFilter == bin_filter) { 445 } else if (selectedFilter == bin_filter) {
445 auto& gpu = Core::System::GetInstance().GPU(); 446 auto& gpu = Core::System::GetInstance().GPU();
446 boost::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); 447 std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
447 448
448 const u8* buffer = Memory::GetPointer(*address); 449 const u8* buffer = Memory::GetPointer(*address);
449 ASSERT_MSG(buffer != nullptr, "Memory not accessible"); 450 ASSERT_MSG(buffer != nullptr, "Memory not accessible");
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 4a09da685..0c831c9f4 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -9,8 +9,8 @@
9#include "core/core.h" 9#include "core/core.h"
10#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/handle_table.h" 11#include "core/hle/kernel/handle_table.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/mutex.h" 12#include "core/hle/kernel/mutex.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/scheduler.h" 14#include "core/hle/kernel/scheduler.h"
15#include "core/hle/kernel/thread.h" 15#include "core/hle/kernel/thread.h"
16#include "core/hle/kernel/timer.h" 16#include "core/hle/kernel/timer.h"
@@ -66,10 +66,11 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
66 } 66 }
67 }; 67 };
68 68
69 add_threads(Core::System::GetInstance().Scheduler(0)->GetThreadList()); 69 const auto& system = Core::System::GetInstance();
70 add_threads(Core::System::GetInstance().Scheduler(1)->GetThreadList()); 70 add_threads(system.Scheduler(0).GetThreadList());
71 add_threads(Core::System::GetInstance().Scheduler(2)->GetThreadList()); 71 add_threads(system.Scheduler(1).GetThreadList());
72 add_threads(Core::System::GetInstance().Scheduler(3)->GetThreadList()); 72 add_threads(system.Scheduler(2).GetThreadList());
73 add_threads(system.Scheduler(3).GetThreadList());
73 74
74 return item_list; 75 return item_list;
75} 76}
@@ -82,7 +83,7 @@ QString WaitTreeText::GetText() const {
82} 83}
83 84
84WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) { 85WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) {
85 auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); 86 const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
86 87
87 mutex_value = Memory::Read32(mutex_address); 88 mutex_value = Memory::Read32(mutex_address);
88 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask); 89 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index defbf734f..331f89885 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -11,7 +11,6 @@
11#include <QAbstractItemModel> 11#include <QAbstractItemModel>
12#include <QDockWidget> 12#include <QDockWidget>
13#include <QTreeView> 13#include <QTreeView>
14#include <boost/container/flat_set.hpp>
15#include "common/common_types.h" 14#include "common/common_types.h"
16#include "core/hle/kernel/object.h" 15#include "core/hle/kernel/object.h"
17 16
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 67890455a..a5a4aa432 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -16,7 +16,6 @@
16#include <fmt/format.h> 16#include <fmt/format.h>
17#include "common/common_paths.h" 17#include "common/common_paths.h"
18#include "common/common_types.h" 18#include "common/common_types.h"
19#include "common/file_util.h"
20#include "common/logging/log.h" 19#include "common/logging/log.h"
21#include "core/file_sys/patch_manager.h" 20#include "core/file_sys/patch_manager.h"
22#include "yuzu/compatibility_list.h" 21#include "yuzu/compatibility_list.h"
@@ -217,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
217 tree_view->setContextMenuPolicy(Qt::CustomContextMenu); 216 tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
218 217
219 item_model->insertColumns(0, COLUMN_COUNT); 218 item_model->insertColumns(0, COLUMN_COUNT);
220 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); 219 item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
221 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); 220 item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
222 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); 221 item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons"));
223 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); 222 item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type"));
224 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); 223 item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size"));
225 224
226 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); 225 connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
227 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); 226 connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
@@ -387,9 +386,9 @@ void GameList::LoadCompatibilityList() {
387} 386}
388 387
389void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { 388void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
390 if (!FileUtil::Exists(dir_path.toStdString()) || 389 const QFileInfo dir_info{dir_path};
391 !FileUtil::IsDirectory(dir_path.toStdString())) { 390 if (!dir_info.exists() || !dir_info.isDir()) {
392 LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); 391 LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString());
393 search_field->setFilterResult(0, 0); 392 search_field->setFilterResult(0, 0);
394 return; 393 return;
395 } 394 }
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 1947bdb93..3d865a12d 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -27,9 +27,8 @@
27#include "yuzu/ui_settings.h" 27#include "yuzu/ui_settings.h"
28 28
29namespace { 29namespace {
30void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, 30void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca,
31 const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon, 31 std::vector<u8>& icon, std::string& name) {
32 std::string& name) {
33 auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca); 32 auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
34 if (icon_file != nullptr) 33 if (icon_file != nullptr)
35 icon = icon_file->ReadAllBytes(); 34 icon = icon_file->ReadAllBytes();
@@ -57,16 +56,30 @@ QString FormatGameName(const std::string& physical_name) {
57 return physical_name_as_qstring; 56 return physical_name_as_qstring;
58} 57}
59 58
60QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) { 59QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
60 Loader::AppLoader& loader, bool updatable = true) {
61 QString out; 61 QString out;
62 for (const auto& kv : patch_manager.GetPatchVersionNames()) { 62 FileSys::VirtualFile update_raw;
63 if (!updatable && kv.first == "Update") 63 loader.ReadUpdateRaw(update_raw);
64 for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) {
65 const bool is_update = kv.first == "Update";
66 if (!updatable && is_update) {
64 continue; 67 continue;
68 }
69
70 const QString type = QString::fromStdString(kv.first);
65 71
66 if (kv.second.empty()) { 72 if (kv.second.empty()) {
67 out.append(fmt::format("{}\n", kv.first).c_str()); 73 out.append(QStringLiteral("%1\n").arg(type));
68 } else { 74 } else {
69 out.append(fmt::format("{} ({})\n", kv.first, kv.second).c_str()); 75 auto ver = kv.second;
76
77 // Display container name for packed updates
78 if (is_update && ver == "PACKED") {
79 ver = Loader::GetFileTypeString(loader.GetFileType());
80 }
81
82 out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver)));
70 } 83 }
71 } 84 }
72 85
@@ -88,7 +101,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
88 FileSys::ContentRecordType::Program); 101 FileSys::ContentRecordType::Program);
89 102
90 for (const auto& game : installed_games) { 103 for (const auto& game : installed_games) {
91 const auto& file = cache->GetEntryUnparsed(game); 104 const auto file = cache->GetEntryUnparsed(game);
92 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 105 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
93 if (!loader) 106 if (!loader)
94 continue; 107 continue;
@@ -99,9 +112,9 @@ void GameListWorker::AddInstalledTitlesToGameList() {
99 loader->ReadProgramId(program_id); 112 loader->ReadProgramId(program_id);
100 113
101 const FileSys::PatchManager patch{program_id}; 114 const FileSys::PatchManager patch{program_id};
102 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); 115 const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
103 if (control != nullptr) 116 if (control != nullptr)
104 GetMetadataFromControlNCA(patch, control, icon, name); 117 GetMetadataFromControlNCA(patch, *control, icon, name);
105 118
106 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); 119 auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
107 120
@@ -116,7 +129,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
116 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 129 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
117 program_id), 130 program_id),
118 new GameListItemCompat(compatibility), 131 new GameListItemCompat(compatibility),
119 new GameListItem(FormatPatchNameVersions(patch)), 132 new GameListItem(FormatPatchNameVersions(patch, *loader)),
120 new GameListItem( 133 new GameListItem(
121 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 134 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
122 new GameListItemSize(file->GetSize()), 135 new GameListItemSize(file->GetSize()),
@@ -127,9 +140,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {
127 FileSys::ContentRecordType::Control); 140 FileSys::ContentRecordType::Control);
128 141
129 for (const auto& entry : control_data) { 142 for (const auto& entry : control_data) {
130 const auto nca = cache->GetEntry(entry); 143 auto nca = cache->GetEntry(entry);
131 if (nca != nullptr) 144 if (nca != nullptr) {
132 nca_control_map.insert_or_assign(entry.title_id, nca); 145 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
146 }
133 } 147 }
134} 148}
135 149
@@ -145,9 +159,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
145 QFileInfo file_info(physical_name.c_str()); 159 QFileInfo file_info(physical_name.c_str());
146 if (!is_dir && file_info.suffix().toStdString() == "nca") { 160 if (!is_dir && file_info.suffix().toStdString() == "nca") {
147 auto nca = 161 auto nca =
148 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 162 std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
149 if (nca->GetType() == FileSys::NCAContentType::Control) 163 if (nca->GetType() == FileSys::NCAContentType::Control) {
150 nca_control_map.insert_or_assign(nca->GetTitleId(), nca); 164 const u64 title_id = nca->GetTitleId();
165 nca_control_map.insert_or_assign(title_id, std::move(nca));
166 }
151 } 167 }
152 return true; 168 return true;
153 }; 169 };
@@ -188,8 +204,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
188 res2 == Loader::ResultStatus::Success) { 204 res2 == Loader::ResultStatus::Success) {
189 // Use from metadata pool. 205 // Use from metadata pool.
190 if (nca_control_map.find(program_id) != nca_control_map.end()) { 206 if (nca_control_map.find(program_id) != nca_control_map.end()) {
191 const auto nca = nca_control_map[program_id]; 207 const auto& nca = nca_control_map[program_id];
192 GetMetadataFromControlNCA(patch, nca, icon, name); 208 GetMetadataFromControlNCA(patch, *nca, icon, name);
193 } 209 }
194 } 210 }
195 211
@@ -206,7 +222,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
206 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), 222 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
207 program_id), 223 program_id),
208 new GameListItemCompat(compatibility), 224 new GameListItemCompat(compatibility),
209 new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())), 225 new GameListItem(
226 FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
210 new GameListItem( 227 new GameListItem(
211 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 228 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
212 new GameListItemSize(FileUtil::GetSize(physical_name)), 229 new GameListItemSize(FileUtil::GetSize(physical_name)),
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 09d20c42f..0e42d0bde 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -63,7 +63,7 @@ private:
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); 63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
64 64
65 std::shared_ptr<FileSys::VfsFilesystem> vfs; 65 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; 66 std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;
67 QStringList watch_list; 67 QStringList watch_list;
68 QString dir_path; 68 QString dir_path;
69 bool deep_scan; 69 bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 1d06d6c95..74a44be37 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -10,6 +10,7 @@
10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 10// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
11#include "core/file_sys/vfs.h" 11#include "core/file_sys/vfs.h"
12#include "core/file_sys/vfs_real.h" 12#include "core/file_sys/vfs_real.h"
13#include "core/hle/service/acc/profile_manager.h"
13 14
14// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows 15// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows
15// defines. 16// defines.
@@ -29,8 +30,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
29#define QT_NO_OPENGL 30#define QT_NO_OPENGL
30#include <QDesktopWidget> 31#include <QDesktopWidget>
31#include <QDialogButtonBox> 32#include <QDialogButtonBox>
33#include <QFile>
32#include <QFileDialog> 34#include <QFileDialog>
33#include <QMessageBox> 35#include <QMessageBox>
36#include <QtConcurrent/QtConcurrent>
34#include <QtGui> 37#include <QtGui>
35#include <QtWidgets> 38#include <QtWidgets>
36#include <fmt/format.h> 39#include <fmt/format.h>
@@ -59,6 +62,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
59#include "core/hle/kernel/process.h" 62#include "core/hle/kernel/process.h"
60#include "core/hle/service/filesystem/filesystem.h" 63#include "core/hle/service/filesystem/filesystem.h"
61#include "core/hle/service/filesystem/fsp_ldr.h" 64#include "core/hle/service/filesystem/fsp_ldr.h"
65#include "core/hle/service/nfp/nfp.h"
66#include "core/hle/service/sm/sm.h"
62#include "core/loader/loader.h" 67#include "core/loader/loader.h"
63#include "core/perf_stats.h" 68#include "core/perf_stats.h"
64#include "core/settings.h" 69#include "core/settings.h"
@@ -99,6 +104,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
99} 104}
100#endif 105#endif
101 106
107constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
108
102/** 109/**
103 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there 110 * "Callouts" are one-time instructional messages shown to the user. In the config settings, there
104 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the 111 * is a bitfield "callout_flags" options, used to track if a message has already been shown to the
@@ -174,8 +181,11 @@ GMainWindow::GMainWindow()
174 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc)); 181 .arg(Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc));
175 show(); 182 show();
176 183
184 // Gen keys if necessary
185 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
186
177 // Necessary to load titles from nand in gamelist. 187 // Necessary to load titles from nand in gamelist.
178 Service::FileSystem::CreateFactories(vfs); 188 Service::FileSystem::CreateFactories(*vfs);
179 game_list->LoadCompatibilityList(); 189 game_list->LoadCompatibilityList();
180 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 190 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
181 191
@@ -421,6 +431,7 @@ void GMainWindow::ConnectMenuEvents() {
421 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this, 431 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
422 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); }); 432 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
423 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 433 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
434 connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
424 435
425 // Emulation 436 // Emulation
426 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); 437 connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
@@ -446,6 +457,9 @@ void GMainWindow::ConnectMenuEvents() {
446 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 457 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
447 458
448 // Help 459 // Help
460 connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
461 connect(ui.action_Rederive, &QAction::triggered, this,
462 std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
449 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout); 463 connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
450} 464}
451 465
@@ -488,6 +502,8 @@ QStringList GMainWindow::GetUnsupportedGLExtensions() {
488 unsupported_ext.append("ARB_texture_storage"); 502 unsupported_ext.append("ARB_texture_storage");
489 if (!GLAD_GL_ARB_multi_bind) 503 if (!GLAD_GL_ARB_multi_bind)
490 unsupported_ext.append("ARB_multi_bind"); 504 unsupported_ext.append("ARB_multi_bind");
505 if (!GLAD_GL_ARB_copy_image)
506 unsupported_ext.append("ARB_copy_image");
491 507
492 // Extensions required to support some texture formats. 508 // Extensions required to support some texture formats.
493 if (!GLAD_GL_EXT_texture_compression_s3tc) 509 if (!GLAD_GL_EXT_texture_compression_s3tc)
@@ -685,6 +701,7 @@ void GMainWindow::ShutdownGame() {
685 ui.action_Stop->setEnabled(false); 701 ui.action_Stop->setEnabled(false);
686 ui.action_Restart->setEnabled(false); 702 ui.action_Restart->setEnabled(false);
687 ui.action_Report_Compatibility->setEnabled(false); 703 ui.action_Report_Compatibility->setEnabled(false);
704 ui.action_Load_Amiibo->setEnabled(false);
688 render_window->hide(); 705 render_window->hide();
689 game_list->show(); 706 game_list->show();
690 game_list->setFilterFocus(); 707 game_list->setFilterFocus();
@@ -746,12 +763,43 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
746 open_target = "Save Data"; 763 open_target = "Save Data";
747 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); 764 const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
748 ASSERT(program_id != 0); 765 ASSERT(program_id != 0);
749 // TODO(tech4me): Update this to work with arbitrary user profile 766
750 // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor 767 Service::Account::ProfileManager manager{};
751 constexpr u128 user_id = {1, 0}; 768 const auto user_ids = manager.GetAllUsers();
769 QStringList list;
770 for (const auto& user_id : user_ids) {
771 if (user_id == Service::Account::UUID{})
772 continue;
773 Service::Account::ProfileBase base;
774 if (!manager.GetProfileBase(user_id, base))
775 continue;
776
777 list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer(
778 reinterpret_cast<const char*>(base.username.data()), base.username.size())));
779 }
780
781 bool ok = false;
782 const auto index_string =
783 QInputDialog::getItem(this, tr("Select User"),
784 tr("Please select the user's save data you would like to open."),
785 list, Settings::values.current_user, false, &ok);
786 if (!ok)
787 return;
788
789 const auto index = list.indexOf(index_string);
790 ASSERT(index != -1 && index < 8);
791
792 const auto user_id = manager.GetUser(index);
793 ASSERT(user_id);
752 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, 794 path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
753 FileSys::SaveDataType::SaveData, 795 FileSys::SaveDataType::SaveData,
754 program_id, user_id, 0); 796 program_id, user_id->uuid, 0);
797
798 if (!FileUtil::Exists(path)) {
799 FileUtil::CreateFullPath(path);
800 FileUtil::CreateDir(path);
801 }
802
755 break; 803 break;
756 } 804 }
757 case GameListOpenTarget::ModData: { 805 case GameListOpenTarget::ModData: {
@@ -818,14 +866,10 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src
818} 866}
819 867
820void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { 868void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
821 const auto path = fmt::format("{}{:016X}/romfs", 869 const auto failed = [this] {
822 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
823
824 const auto failed = [this, &path] {
825 QMessageBox::warning(this, tr("RomFS Extraction Failed!"), 870 QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
826 tr("There was an error copying the RomFS files or the user " 871 tr("There was an error copying the RomFS files or the user "
827 "cancelled the operation.")); 872 "cancelled the operation."));
828 vfs->DeleteDirectory(path);
829 }; 873 };
830 874
831 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); 875 const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
@@ -840,10 +884,24 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
840 return; 884 return;
841 } 885 }
842 886
843 const auto romfs = 887 const auto installed = Service::FileSystem::GetUnionContents();
844 loader->IsRomFSUpdatable() 888 auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
845 ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) 889
846 : file; 890 if (!romfs_title_id) {
891 failed();
892 return;
893 }
894
895 const auto path = fmt::format(
896 "{}{:016X}/romfs", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), *romfs_title_id);
897
898 FileSys::VirtualFile romfs;
899
900 if (*romfs_title_id == program_id) {
901 romfs = file;
902 } else {
903 romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
904 }
847 905
848 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); 906 const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
849 if (extracted == nullptr) { 907 if (extracted == nullptr) {
@@ -855,6 +913,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
855 913
856 if (out == nullptr) { 914 if (out == nullptr) {
857 failed(); 915 failed();
916 vfs->DeleteDirectory(path);
858 return; 917 return;
859 } 918 }
860 919
@@ -865,8 +924,11 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
865 "files into the new directory while <br>skeleton will only create the directory " 924 "files into the new directory while <br>skeleton will only create the directory "
866 "structure."), 925 "structure."),
867 {"Full", "Skeleton"}, 0, false, &ok); 926 {"Full", "Skeleton"}, 0, false, &ok);
868 if (!ok) 927 if (!ok) {
869 failed(); 928 failed();
929 vfs->DeleteDirectory(path);
930 return;
931 }
870 932
871 const auto full = res == "Full"; 933 const auto full = res == "Full";
872 const auto entry_size = CalculateRomFSEntrySize(extracted, full); 934 const auto entry_size = CalculateRomFSEntrySize(extracted, full);
@@ -883,6 +945,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
883 } else { 945 } else {
884 progress.close(); 946 progress.close();
885 failed(); 947 failed();
948 vfs->DeleteDirectory(path);
886 } 949 }
887} 950}
888 951
@@ -903,22 +966,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
903} 966}
904 967
905void GMainWindow::OnMenuLoadFile() { 968void GMainWindow::OnMenuLoadFile() {
906 QString extensions; 969 const QString extensions =
907 for (const auto& piece : game_list->supported_file_extensions) 970 QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
908 extensions += "*." + piece + " "; 971 const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)",
909 972 "%1 is an identifier for the Switch executable file extensions.")
910 extensions += "main "; 973 .arg(extensions);
911 974 const QString filename = QFileDialog::getOpenFileName(
912 QString file_filter = tr("Switch Executable") + " (" + extensions + ")"; 975 this, tr("Load File"), UISettings::values.roms_path, file_filter);
913 file_filter += ";;" + tr("All Files (*.*)");
914
915 QString filename = QFileDialog::getOpenFileName(this, tr("Load File"),
916 UISettings::values.roms_path, file_filter);
917 if (!filename.isEmpty()) {
918 UISettings::values.roms_path = QFileInfo(filename).path();
919 976
920 BootGame(filename); 977 if (filename.isEmpty()) {
978 return;
921 } 979 }
980
981 UISettings::values.roms_path = QFileInfo(filename).path();
982 BootGame(filename);
922} 983}
923 984
924void GMainWindow::OnMenuLoadFolder() { 985void GMainWindow::OnMenuLoadFolder() {
@@ -1134,7 +1195,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1134 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1195 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1135 : FileUtil::UserPath::NANDDir, 1196 : FileUtil::UserPath::NANDDir,
1136 dir_path.toStdString()); 1197 dir_path.toStdString());
1137 Service::FileSystem::CreateFactories(vfs); 1198 Service::FileSystem::CreateFactories(*vfs);
1138 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); 1199 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1139 } 1200 }
1140} 1201}
@@ -1171,6 +1232,7 @@ void GMainWindow::OnStartGame() {
1171 ui.action_Report_Compatibility->setEnabled(true); 1232 ui.action_Report_Compatibility->setEnabled(true);
1172 1233
1173 discord_rpc->Update(); 1234 discord_rpc->Update();
1235 ui.action_Load_Amiibo->setEnabled(true);
1174} 1236}
1175 1237
1176void GMainWindow::OnPauseGame() { 1238void GMainWindow::OnPauseGame() {
@@ -1275,6 +1337,52 @@ void GMainWindow::OnConfigure() {
1275 } 1337 }
1276} 1338}
1277 1339
1340void GMainWindow::OnLoadAmiibo() {
1341 const QString extensions{"*.bin"};
1342 const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
1343 const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
1344
1345 if (filename.isEmpty()) {
1346 return;
1347 }
1348
1349 Core::System& system{Core::System::GetInstance()};
1350 Service::SM::ServiceManager& sm = system.ServiceManager();
1351 auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
1352 if (nfc == nullptr) {
1353 return;
1354 }
1355
1356 QFile nfc_file{filename};
1357 if (!nfc_file.open(QIODevice::ReadOnly)) {
1358 QMessageBox::warning(this, tr("Error opening Amiibo data file"),
1359 tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename));
1360 return;
1361 }
1362
1363 const u64 nfc_file_size = nfc_file.size();
1364 std::vector<u8> buffer(nfc_file_size);
1365 const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size);
1366 if (nfc_file_size != read_size) {
1367 QMessageBox::warning(this, tr("Error reading Amiibo data file"),
1368 tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but "
1369 "was only able to read %2 bytes.")
1370 .arg(nfc_file_size)
1371 .arg(read_size));
1372 return;
1373 }
1374
1375 if (!nfc->LoadAmiibo(buffer)) {
1376 QMessageBox::warning(this, tr("Error loading Amiibo data"),
1377 tr("Unable to load Amiibo data."));
1378 }
1379}
1380
1381void GMainWindow::OnOpenYuzuFolder() {
1382 QDesktopServices::openUrl(QUrl::fromLocalFile(
1383 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
1384}
1385
1278void GMainWindow::OnAbout() { 1386void GMainWindow::OnAbout() {
1279 AboutDialog aboutDialog(this); 1387 AboutDialog aboutDialog(this);
1280 aboutDialog.exec(); 1388 aboutDialog.exec();
@@ -1315,15 +1423,17 @@ void GMainWindow::UpdateStatusBar() {
1315void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { 1423void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
1316 QMessageBox::StandardButton answer; 1424 QMessageBox::StandardButton answer;
1317 QString status_message; 1425 QString status_message;
1318 const QString common_message = tr( 1426 const QString common_message =
1319 "The game you are trying to load requires additional files from your Switch to be dumped " 1427 tr("The game you are trying to load requires additional files from your Switch to be "
1320 "before playing.<br/><br/>For more information on dumping these files, please see the " 1428 "dumped "
1321 "following wiki page: <a " 1429 "before playing.<br/><br/>For more information on dumping these files, please see the "
1322 "href='https://yuzu-emu.org/wiki/" 1430 "following wiki page: <a "
1323 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System " 1431 "href='https://yuzu-emu.org/wiki/"
1324 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit " 1432 "dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
1325 "back to the game list? Continuing emulation may result in crashes, corrupted save " 1433 "Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
1326 "data, or other bugs."); 1434 "quit "
1435 "back to the game list? Continuing emulation may result in crashes, corrupted save "
1436 "data, or other bugs.");
1327 switch (result) { 1437 switch (result) {
1328 case Core::System::ResultStatus::ErrorSystemFiles: { 1438 case Core::System::ResultStatus::ErrorSystemFiles: {
1329 QString message = "yuzu was unable to locate a Switch system archive"; 1439 QString message = "yuzu was unable to locate a Switch system archive";
@@ -1354,9 +1464,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1354 this, tr("Fatal Error"), 1464 this, tr("Fatal Error"),
1355 tr("yuzu has encountered a fatal error, please see the log for more details. " 1465 tr("yuzu has encountered a fatal error, please see the log for more details. "
1356 "For more information on accessing the log, please see the following page: " 1466 "For more information on accessing the log, please see the following page: "
1357 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to " 1467 "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
1358 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? " 1468 "to "
1359 "Continuing emulation may result in crashes, corrupted save data, or other bugs."), 1469 "Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
1470 "list? "
1471 "Continuing emulation may result in crashes, corrupted save data, or other "
1472 "bugs."),
1360 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 1473 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
1361 status_message = "Fatal Error encountered"; 1474 status_message = "Fatal Error encountered";
1362 break; 1475 break;
@@ -1376,6 +1489,122 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
1376 } 1489 }
1377} 1490}
1378 1491
1492void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1493 if (behavior == ReinitializeKeyBehavior::Warning) {
1494 const auto res = QMessageBox::information(
1495 this, tr("Confirm Key Rederivation"),
1496 tr("You are about to force rederive all of your keys. \nIf you do not know what this "
1497 "means or what you are doing, \nthis is a potentially destructive action. \nPlease "
1498 "make "
1499 "sure this is what you want \nand optionally make backups.\n\nThis will delete your "
1500 "autogenerated key files and re-run the key derivation module."),
1501 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
1502
1503 if (res == QMessageBox::Cancel)
1504 return;
1505
1506 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1507 "prod.keys_autogenerated");
1508 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1509 "console.keys_autogenerated");
1510 FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::KeysDir) +
1511 "title.keys_autogenerated");
1512 }
1513
1514 Core::Crypto::KeyManager keys{};
1515 if (keys.BaseDeriveNecessary()) {
1516 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory(
1517 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), FileSys::Mode::Read)};
1518
1519 const auto function = [this, &keys, &pdm] {
1520 keys.PopulateFromPartitionData(pdm);
1521 Service::FileSystem::CreateFactories(*vfs);
1522 keys.DeriveETicket(pdm);
1523 };
1524
1525 QString errors;
1526
1527 if (!pdm.HasFuses())
1528 errors += tr("- Missing fuses - Cannot derive SBK\n");
1529 if (!pdm.HasBoot0())
1530 errors += tr("- Missing BOOT0 - Cannot derive master keys\n");
1531 if (!pdm.HasPackage2())
1532 errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n");
1533 if (!pdm.HasProdInfo())
1534 errors += tr("- Missing PRODINFO - Cannot derive title keys\n");
1535
1536 if (!errors.isEmpty()) {
1537
1538 QMessageBox::warning(
1539 this, tr("Warning Missing Derivation Components"),
1540 tr("The following are missing from your configuration that may hinder key "
1541 "derivation. It will be attempted but may not complete.<br><br>") +
1542 errors +
1543 tr("<br><br>You can get all of these and dump all of your games easily by "
1544 "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
1545 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1546 "to obtain all of your keys."));
1547 }
1548
1549 QProgressDialog prog;
1550 prog.setRange(0, 0);
1551 prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
1552 "system's performance."));
1553 prog.setWindowTitle(tr("Deriving Keys"));
1554
1555 prog.show();
1556
1557 auto future = QtConcurrent::run(function);
1558 while (!future.isFinished()) {
1559 QCoreApplication::processEvents();
1560 }
1561
1562 prog.close();
1563 }
1564
1565 Service::FileSystem::CreateFactories(*vfs);
1566
1567 if (behavior == ReinitializeKeyBehavior::Warning) {
1568 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
1569 }
1570}
1571
1572std::optional<u64> GMainWindow::SelectRomFSDumpTarget(
1573 const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
1574 const auto dlc_entries =
1575 installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
1576 std::vector<FileSys::RegisteredCacheEntry> dlc_match;
1577 dlc_match.reserve(dlc_entries.size());
1578 std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
1579 [&program_id, &installed](const FileSys::RegisteredCacheEntry& entry) {
1580 return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id &&
1581 installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
1582 });
1583
1584 std::vector<u64> romfs_tids;
1585 romfs_tids.push_back(program_id);
1586 for (const auto& entry : dlc_match)
1587 romfs_tids.push_back(entry.title_id);
1588
1589 if (romfs_tids.size() > 1) {
1590 QStringList list{"Base"};
1591 for (std::size_t i = 1; i < romfs_tids.size(); ++i)
1592 list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF));
1593
1594 bool ok;
1595 const auto res = QInputDialog::getItem(
1596 this, tr("Select RomFS Dump Target"),
1597 tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
1598 if (!ok) {
1599 return {};
1600 }
1601
1602 return romfs_tids[list.indexOf(res)];
1603 }
1604
1605 return program_id;
1606}
1607
1379bool GMainWindow::ConfirmClose() { 1608bool GMainWindow::ConfirmClose() {
1380 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) 1609 if (emu_thread == nullptr || !UISettings::values.confirm_before_closing)
1381 return true; 1610 return true;
@@ -1484,7 +1713,7 @@ void GMainWindow::UpdateUITheme() {
1484 emit UpdateThemedIcons(); 1713 emit UpdateThemedIcons();
1485} 1714}
1486 1715
1487void GMainWindow::SetDiscordEnabled(bool state) { 1716void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
1488#ifdef USE_DISCORD_PRESENCE 1717#ifdef USE_DISCORD_PRESENCE
1489 if (state) { 1718 if (state) {
1490 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); 1719 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index fe0e9a50a..929250e8c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <optional>
8#include <unordered_map> 9#include <unordered_map>
9 10
10#include <QMainWindow> 11#include <QMainWindow>
@@ -29,8 +30,9 @@ class WaitTreeWidget;
29enum class GameListOpenTarget; 30enum class GameListOpenTarget;
30 31
31namespace FileSys { 32namespace FileSys {
33class RegisteredCacheUnion;
32class VfsFilesystem; 34class VfsFilesystem;
33} 35} // namespace FileSys
34 36
35namespace Tegra { 37namespace Tegra {
36class DebugContext; 38class DebugContext;
@@ -41,6 +43,11 @@ enum class EmulatedDirectoryTarget {
41 SDMC, 43 SDMC,
42}; 44};
43 45
46enum class ReinitializeKeyBehavior {
47 NoWarning,
48 Warning,
49};
50
44namespace DiscordRPC { 51namespace DiscordRPC {
45class DiscordInterface; 52class DiscordInterface;
46} 53}
@@ -159,6 +166,8 @@ private slots:
159 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target); 166 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
160 void OnMenuRecentFile(); 167 void OnMenuRecentFile();
161 void OnConfigure(); 168 void OnConfigure();
169 void OnLoadAmiibo();
170 void OnOpenYuzuFolder();
162 void OnAbout(); 171 void OnAbout();
163 void OnToggleFilterBar(); 172 void OnToggleFilterBar();
164 void OnDisplayTitleBars(bool); 173 void OnDisplayTitleBars(bool);
@@ -167,8 +176,10 @@ private slots:
167 void HideFullscreen(); 176 void HideFullscreen();
168 void ToggleWindowMode(); 177 void ToggleWindowMode();
169 void OnCoreError(Core::System::ResultStatus, std::string); 178 void OnCoreError(Core::System::ResultStatus, std::string);
179 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
170 180
171private: 181private:
182 std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id);
172 void UpdateStatusBar(); 183 void UpdateStatusBar();
173 184
174 Ui::MainWindow ui; 185 Ui::MainWindow ui;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index cb1664b21..28cf269e7 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -57,8 +57,8 @@
57 <string>Recent Files</string> 57 <string>Recent Files</string>
58 </property> 58 </property>
59 </widget> 59 </widget>
60 <addaction name="action_Install_File_NAND" /> 60 <addaction name="action_Install_File_NAND"/>
61 <addaction name="separator"/> 61 <addaction name="separator"/>
62 <addaction name="action_Load_File"/> 62 <addaction name="action_Load_File"/>
63 <addaction name="action_Load_Folder"/> 63 <addaction name="action_Load_Folder"/>
64 <addaction name="separator"/> 64 <addaction name="separator"/>
@@ -68,6 +68,8 @@
68 <addaction name="action_Select_NAND_Directory"/> 68 <addaction name="action_Select_NAND_Directory"/>
69 <addaction name="action_Select_SDMC_Directory"/> 69 <addaction name="action_Select_SDMC_Directory"/>
70 <addaction name="separator"/> 70 <addaction name="separator"/>
71 <addaction name="action_Load_Amiibo"/>
72 <addaction name="separator"/>
71 <addaction name="action_Exit"/> 73 <addaction name="action_Exit"/>
72 </widget> 74 </widget>
73 <widget class="QMenu" name="menu_Emulation"> 75 <widget class="QMenu" name="menu_Emulation">
@@ -97,24 +99,35 @@
97 <addaction name="action_Show_Status_Bar"/> 99 <addaction name="action_Show_Status_Bar"/>
98 <addaction name="menu_View_Debugging"/> 100 <addaction name="menu_View_Debugging"/>
99 </widget> 101 </widget>
102 <widget class ="QMenu" name="menu_Tools">
103 <property name="title">
104 <string>Tools</string>
105 </property>
106 <addaction name="action_Rederive" />
107 </widget>
100 <widget class="QMenu" name="menu_Help"> 108 <widget class="QMenu" name="menu_Help">
101 <property name="title"> 109 <property name="title">
102 <string>&amp;Help</string> 110 <string>&amp;Help</string>
103 </property> 111 </property>
104 <addaction name="action_Report_Compatibility"/> 112 <addaction name="action_Report_Compatibility"/>
113 <addaction name="action_Open_yuzu_Folder" />
105 <addaction name="separator"/> 114 <addaction name="separator"/>
106 <addaction name="action_About"/> 115 <addaction name="action_About"/>
107 </widget> 116 </widget>
108 <addaction name="menu_File"/> 117 <addaction name="menu_File"/>
109 <addaction name="menu_Emulation"/> 118 <addaction name="menu_Emulation"/>
110 <addaction name="menu_View"/> 119 <addaction name="menu_View"/>
120 <addaction name="menu_Tools" />
111 <addaction name="menu_Help"/> 121 <addaction name="menu_Help"/>
112 </widget> 122 </widget>
113 <action name="action_Install_File_NAND"> 123 <action name="action_Install_File_NAND">
114 <property name="text"> 124 <property name="enabled">
115 <string>Install File to NAND...</string> 125 <bool>true</bool>
116 </property> 126 </property>
117 </action> 127 <property name="text">
128 <string>Install File to NAND...</string>
129 </property>
130 </action>
118 <action name="action_Load_File"> 131 <action name="action_Load_File">
119 <property name="text"> 132 <property name="text">
120 <string>Load File...</string> 133 <string>Load File...</string>
@@ -159,6 +172,11 @@
159 <string>&amp;Stop</string> 172 <string>&amp;Stop</string>
160 </property> 173 </property>
161 </action> 174 </action>
175 <action name="action_Rederive">
176 <property name="text">
177 <string>Reinitialize keys...</string>
178 </property>
179 </action>
162 <action name="action_About"> 180 <action name="action_About">
163 <property name="text"> 181 <property name="text">
164 <string>About yuzu</string> 182 <string>About yuzu</string>
@@ -241,6 +259,14 @@
241 <string>Restart</string> 259 <string>Restart</string>
242 </property> 260 </property>
243 </action> 261 </action>
262 <action name="action_Load_Amiibo">
263 <property name="enabled">
264 <bool>false</bool>
265 </property>
266 <property name="text">
267 <string>Load Amiibo...</string>
268 </property>
269 </action>
244 <action name="action_Report_Compatibility"> 270 <action name="action_Report_Compatibility">
245 <property name="enabled"> 271 <property name="enabled">
246 <bool>false</bool> 272 <bool>false</bool>
@@ -252,6 +278,11 @@
252 <bool>false</bool> 278 <bool>false</bool>
253 </property> 279 </property>
254 </action> 280 </action>
281 <action name="action_Open_yuzu_Folder">
282 <property name="text">
283 <string>Open yuzu Folder</string>
284 </property>
285 </action>
255 </widget> 286 </widget>
256 <resources/> 287 <resources/>
257 <connections/> 288 <connections/>
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp
new file mode 100644
index 000000000..edd78e579
--- /dev/null
+++ b/src/yuzu/util/limitable_input_dialog.cpp
@@ -0,0 +1,59 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QDialogButtonBox>
6#include <QLabel>
7#include <QLineEdit>
8#include <QPushButton>
9#include <QVBoxLayout>
10#include "yuzu/util/limitable_input_dialog.h"
11
12LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
13 CreateUI();
14 ConnectEvents();
15}
16
17LimitableInputDialog::~LimitableInputDialog() = default;
18
19void LimitableInputDialog::CreateUI() {
20 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
21
22 text_label = new QLabel(this);
23 text_entry = new QLineEdit(this);
24 buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
25
26 auto* const layout = new QVBoxLayout;
27 layout->addWidget(text_label);
28 layout->addWidget(text_entry);
29 layout->addWidget(buttons);
30
31 setLayout(layout);
32}
33
34void LimitableInputDialog::ConnectEvents() {
35 connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
36 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
37}
38
39QString LimitableInputDialog::GetText(QWidget* parent, const QString& title, const QString& text,
40 int min_character_limit, int max_character_limit) {
41 Q_ASSERT(min_character_limit <= max_character_limit);
42
43 LimitableInputDialog dialog{parent};
44 dialog.setWindowTitle(title);
45 dialog.text_label->setText(text);
46 dialog.text_entry->setMaxLength(max_character_limit);
47
48 auto* const ok_button = dialog.buttons->button(QDialogButtonBox::Ok);
49 ok_button->setEnabled(false);
50 connect(dialog.text_entry, &QLineEdit::textEdited, [&](const QString& new_text) {
51 ok_button->setEnabled(new_text.length() >= min_character_limit);
52 });
53
54 if (dialog.exec() != QDialog::Accepted) {
55 return {};
56 }
57
58 return dialog.text_entry->text();
59}
diff --git a/src/yuzu/util/limitable_input_dialog.h b/src/yuzu/util/limitable_input_dialog.h
new file mode 100644
index 000000000..164ad7301
--- /dev/null
+++ b/src/yuzu/util/limitable_input_dialog.h
@@ -0,0 +1,31 @@
1// Copyright 2018 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <QDialog>
8
9class QDialogButtonBox;
10class QLabel;
11class QLineEdit;
12
13/// A QDialog that functions similarly to QInputDialog, however, it allows
14/// restricting the minimum and total number of characters that can be entered.
15class LimitableInputDialog final : public QDialog {
16 Q_OBJECT
17public:
18 explicit LimitableInputDialog(QWidget* parent = nullptr);
19 ~LimitableInputDialog() override;
20
21 static QString GetText(QWidget* parent, const QString& title, const QString& text,
22 int min_character_limit, int max_character_limit);
23
24private:
25 void CreateUI();
26 void ConnectEvents();
27
28 QLabel* text_label;
29 QLineEdit* text_entry;
30 QDialogButtonBox* buttons;
31};
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 9d934e220..b456266a6 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -8,6 +8,7 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/param_package.h" 10#include "common/param_package.h"
11#include "core/hle/service/acc/profile_manager.h"
11#include "core/settings.h" 12#include "core/settings.h"
12#include "input_common/main.h" 13#include "input_common/main.h"
13#include "yuzu_cmd/config.h" 14#include "yuzu_cmd/config.h"
@@ -99,8 +100,8 @@ void Config::ReadValues() {
99 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); 100 Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
100 Settings::values.frame_limit = 101 Settings::values.frame_limit =
101 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); 102 static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100));
102 Settings::values.use_accurate_framebuffers = 103 Settings::values.use_accurate_gpu_emulation =
103 sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false); 104 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
104 105
105 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0); 106 Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);
106 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 107 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
@@ -125,10 +126,11 @@ void Config::ReadValues() {
125 126
126 // System 127 // System
127 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 128 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
128 Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); 129 Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
129 if (Settings::values.username.empty()) { 130 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
130 Settings::values.username = "yuzu"; 131
131 } 132 Settings::values.current_user = std::clamp<int>(
133 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
132 134
133 // Miscellaneous 135 // Miscellaneous
134 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 136 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
@@ -138,6 +140,7 @@ void Config::ReadValues() {
138 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); 140 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
139 Settings::values.gdbstub_port = 141 Settings::values.gdbstub_port =
140 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 142 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
143 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
141 144
142 // Web Service 145 // Web Service
143 Settings::values.enable_telemetry = 146 Settings::values.enable_telemetry =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 762396e3b..e0b223cd6 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -110,9 +110,9 @@ use_frame_limit =
110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) 110# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
111frame_limit = 111frame_limit =
112 112
113# Whether to use accurate framebuffers 113# Whether to use accurate GPU emulation
114# 0 (default): Off (fast), 1 : On (slow) 114# 0 (default): Off (fast), 1 : On (slow)
115use_accurate_framebuffers = 115use_accurate_gpu_emulation =
116 116
117# The clear color for the renderer. What shows up on the sides of the bottom screen. 117# The clear color for the renderer. What shows up on the sides of the bottom screen.
118# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 118# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
@@ -174,6 +174,10 @@ use_virtual_sd =
174# 1: Yes, 0 (default): No 174# 1: Yes, 0 (default): No
175use_docked_mode = 175use_docked_mode =
176 176
177# Allow the use of NFC in games
178# 1 (default): Yes, 0 : No
179enable_nfc =
180
177# Sets the account username, max length is 32 characters 181# Sets the account username, max length is 32 characters
178# yuzu (default) 182# yuzu (default)
179username = yuzu 183username = yuzu
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 0733301b2..a9ad92a80 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -40,6 +40,35 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
40 } 40 }
41} 41}
42 42
43std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const {
44 int w, h;
45 SDL_GetWindowSize(render_window, &w, &h);
46
47 touch_x *= w;
48 touch_y *= h;
49
50 return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
51 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
52}
53
54void EmuWindow_SDL2::OnFingerDown(float x, float y) {
55 // TODO(NeatNit): keep track of multitouch using the fingerID and a dictionary of some kind
56 // This isn't critical because the best we can do when we have that is to average them, like the
57 // 3DS does
58
59 const auto [px, py] = TouchToPixelPos(x, y);
60 TouchPressed(px, py);
61}
62
63void EmuWindow_SDL2::OnFingerMotion(float x, float y) {
64 const auto [px, py] = TouchToPixelPos(x, y);
65 TouchMoved(px, py);
66}
67
68void EmuWindow_SDL2::OnFingerUp() {
69 TouchReleased();
70}
71
43void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 72void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
44 if (state == SDL_PRESSED) { 73 if (state == SDL_PRESSED) {
45 InputCommon::GetKeyboard()->PressKey(key); 74 InputCommon::GetKeyboard()->PressKey(key);
@@ -98,6 +127,8 @@ bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
98 unsupported_ext.push_back("ARB_texture_storage"); 127 unsupported_ext.push_back("ARB_texture_storage");
99 if (!GLAD_GL_ARB_multi_bind) 128 if (!GLAD_GL_ARB_multi_bind)
100 unsupported_ext.push_back("ARB_multi_bind"); 129 unsupported_ext.push_back("ARB_multi_bind");
130 if (!GLAD_GL_ARB_copy_image)
131 unsupported_ext.push_back("ARB_copy_image");
101 132
102 // Extensions required to support some texture formats. 133 // Extensions required to support some texture formats.
103 if (!GLAD_GL_EXT_texture_compression_s3tc) 134 if (!GLAD_GL_EXT_texture_compression_s3tc)
@@ -217,11 +248,26 @@ void EmuWindow_SDL2::PollEvents() {
217 OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state); 248 OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
218 break; 249 break;
219 case SDL_MOUSEMOTION: 250 case SDL_MOUSEMOTION:
220 OnMouseMotion(event.motion.x, event.motion.y); 251 // ignore if it came from touch
252 if (event.button.which != SDL_TOUCH_MOUSEID)
253 OnMouseMotion(event.motion.x, event.motion.y);
221 break; 254 break;
222 case SDL_MOUSEBUTTONDOWN: 255 case SDL_MOUSEBUTTONDOWN:
223 case SDL_MOUSEBUTTONUP: 256 case SDL_MOUSEBUTTONUP:
224 OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y); 257 // ignore if it came from touch
258 if (event.button.which != SDL_TOUCH_MOUSEID) {
259 OnMouseButton(event.button.button, event.button.state, event.button.x,
260 event.button.y);
261 }
262 break;
263 case SDL_FINGERDOWN:
264 OnFingerDown(event.tfinger.x, event.tfinger.y);
265 break;
266 case SDL_FINGERMOTION:
267 OnFingerMotion(event.tfinger.x, event.tfinger.y);
268 break;
269 case SDL_FINGERUP:
270 OnFingerUp();
225 break; 271 break;
226 case SDL_QUIT: 272 case SDL_QUIT:
227 is_open = false; 273 is_open = false;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index d34902109..b0d4116cc 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -40,6 +40,18 @@ private:
40 /// Called by PollEvents when a mouse button is pressed or released 40 /// Called by PollEvents when a mouse button is pressed or released
41 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 41 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
42 42
43 /// Translates pixel position (0..1) to pixel positions
44 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const;
45
46 /// Called by PollEvents when a finger starts touching the touchscreen
47 void OnFingerDown(float x, float y);
48
49 /// Called by PollEvents when a finger moves while touching the touchscreen
50 void OnFingerMotion(float x, float y);
51
52 /// Called by PollEvents when a finger stops touching the touchscreen
53 void OnFingerUp();
54
43 /// Called by PollEvents when any event that may cause the window to be resized occurs 55 /// Called by PollEvents when any event that may cause the window to be resized occurs
44 void OnResize(); 56 void OnResize();
45 57
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index bab465c1d..806127b12 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -56,9 +56,10 @@ static void PrintHelp(const char* argv0) {
56 std::cout << "Usage: " << argv0 56 std::cout << "Usage: " << argv0
57 << " [options] <filename>\n" 57 << " [options] <filename>\n"
58 "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" 58 "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n"
59 "-f, --fullscreen Start in fullscreen mode\n" 59 "-f, --fullscreen Start in fullscreen mode\n"
60 "-h, --help Display this help and exit\n" 60 "-h, --help Display this help and exit\n"
61 "-v, --version Output version information and exit\n"; 61 "-v, --version Output version information and exit\n"
62 "-p, --program Pass following string as arguments to executable\n";
62} 63}
63 64
64static void PrintVersion() { 65static void PrintVersion() {
@@ -106,15 +107,13 @@ int main(int argc, char** argv) {
106 bool fullscreen = false; 107 bool fullscreen = false;
107 108
108 static struct option long_options[] = { 109 static struct option long_options[] = {
109 {"gdbport", required_argument, 0, 'g'}, 110 {"gdbport", required_argument, 0, 'g'}, {"fullscreen", no_argument, 0, 'f'},
110 {"fullscreen", no_argument, 0, 'f'}, 111 {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'},
111 {"help", no_argument, 0, 'h'}, 112 {"program", optional_argument, 0, 'p'}, {0, 0, 0, 0},
112 {"version", no_argument, 0, 'v'},
113 {0, 0, 0, 0},
114 }; 113 };
115 114
116 while (optind < argc) { 115 while (optind < argc) {
117 char arg = getopt_long(argc, argv, "g:fhv", long_options, &option_index); 116 char arg = getopt_long(argc, argv, "g:fhvp::", long_options, &option_index);
118 if (arg != -1) { 117 if (arg != -1) {
119 switch (arg) { 118 switch (arg) {
120 case 'g': 119 case 'g':
@@ -138,6 +137,10 @@ int main(int argc, char** argv) {
138 case 'v': 137 case 'v':
139 PrintVersion(); 138 PrintVersion();
140 return 0; 139 return 0;
140 case 'p':
141 Settings::values.program_args = argv[optind];
142 ++optind;
143 break;
141 } 144 }
142 } else { 145 } else {
143#ifdef _WIN32 146#ifdef _WIN32
@@ -175,7 +178,7 @@ int main(int argc, char** argv) {
175 178
176 Core::System& system{Core::System::GetInstance()}; 179 Core::System& system{Core::System::GetInstance()};
177 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 180 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
178 Service::FileSystem::CreateFactories(system.GetFilesystem()); 181 Service::FileSystem::CreateFactories(*system.GetFilesystem());
179 182
180 SCOPE_EXIT({ system.Shutdown(); }); 183 SCOPE_EXIT({ system.Shutdown(); });
181 184