summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/CMakeLists.txt27
-rw-r--r--src/audio_core/audio_out.cpp58
-rw-r--r--src/audio_core/audio_out.h43
-rw-r--r--src/audio_core/audio_renderer.cpp234
-rw-r--r--src/audio_core/audio_renderer.h206
-rw-r--r--src/audio_core/buffer.h45
-rw-r--r--src/audio_core/codec.cpp77
-rw-r--r--src/audio_core/codec.h44
-rw-r--r--src/audio_core/cubeb_sink.cpp200
-rw-r--r--src/audio_core/cubeb_sink.h32
-rw-r--r--src/audio_core/null_sink.h27
-rw-r--r--src/audio_core/sink.h31
-rw-r--r--src/audio_core/sink_details.cpp44
-rw-r--r--src/audio_core/sink_details.h35
-rw-r--r--src/audio_core/sink_stream.h32
-rw-r--r--src/audio_core/stream.cpp127
-rw-r--r--src/audio_core/stream.h102
-rw-r--r--src/common/common_funcs.h4
-rw-r--r--src/common/common_paths.h1
-rw-r--r--src/common/file_util.cpp14
-rw-r--r--src/common/file_util.h3
-rw-r--r--src/common/logging/backend.cpp16
-rw-r--r--src/common/logging/log.h16
-rw-r--r--src/common/logging/text_formatter.h1
-rw-r--r--src/common/math_util.h10
-rw-r--r--src/common/string_util.cpp8
-rw-r--r--src/common/swap.h2
-rw-r--r--src/common/threadsafe_queue.h32
-rw-r--r--src/common/timer.cpp95
-rw-r--r--src/common/timer.h17
-rw-r--r--src/core/CMakeLists.txt86
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp18
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp2
-rw-r--r--src/core/core.cpp38
-rw-r--r--src/core/core.h60
-rw-r--r--src/core/core_cpu.cpp1
-rw-r--r--src/core/core_timing.cpp14
-rw-r--r--src/core/core_timing.h11
-rw-r--r--src/core/crypto/aes_util.cpp115
-rw-r--r--src/core/crypto/aes_util.h64
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp56
-rw-r--r--src/core/crypto/ctr_encryption_layer.h33
-rw-r--r--src/core/crypto/encryption_layer.cpp42
-rw-r--r--src/core/crypto/encryption_layer.h33
-rw-r--r--src/core/crypto/key_manager.cpp208
-rw-r--r--src/core/crypto/key_manager.h120
-rw-r--r--src/core/crypto/sha_util.cpp5
-rw-r--r--src/core/crypto/sha_util.h20
-rw-r--r--src/core/file_sys/card_image.cpp149
-rw-r--r--src/core/file_sys/card_image.h96
-rw-r--r--src/core/file_sys/content_archive.cpp236
-rw-r--r--src/core/file_sys/content_archive.h31
-rw-r--r--src/core/file_sys/partition_filesystem.cpp5
-rw-r--r--src/core/file_sys/romfs.cpp124
-rw-r--r--src/core/file_sys/romfs.h35
-rw-r--r--src/core/file_sys/vfs.cpp43
-rw-r--r--src/core/file_sys/vfs.h23
-rw-r--r--src/core/file_sys/vfs_offset.cpp7
-rw-r--r--src/core/file_sys/vfs_offset.h3
-rw-r--r--src/core/file_sys/vfs_real.cpp6
-rw-r--r--src/core/file_sys/vfs_real.h3
-rw-r--r--src/core/file_sys/vfs_vector.cpp86
-rw-r--r--src/core/file_sys/vfs_vector.h44
-rw-r--r--src/core/gdbstub/gdbstub.cpp175
-rw-r--r--src/core/gdbstub/gdbstub.h8
-rw-r--r--src/core/hle/ipc_helpers.h7
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp38
-rw-r--r--src/core/hle/kernel/address_arbiter.h4
-rw-r--r--src/core/hle/kernel/client_port.cpp17
-rw-r--r--src/core/hle/kernel/client_port.h16
-rw-r--r--src/core/hle/kernel/client_session.cpp2
-rw-r--r--src/core/hle/kernel/client_session.h2
-rw-r--r--src/core/hle/kernel/event.cpp4
-rw-r--r--src/core/hle/kernel/event.h14
-rw-r--r--src/core/hle/kernel/handle_table.cpp1
-rw-r--r--src/core/hle/kernel/handle_table.h2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp7
-rw-r--r--src/core/hle/kernel/hle_ipc.h3
-rw-r--r--src/core/hle/kernel/kernel.cpp8
-rw-r--r--src/core/hle/kernel/kernel.h112
-rw-r--r--src/core/hle/kernel/memory.cpp92
-rw-r--r--src/core/hle/kernel/memory.h31
-rw-r--r--src/core/hle/kernel/mutex.cpp7
-rw-r--r--src/core/hle/kernel/mutex.h8
-rw-r--r--src/core/hle/kernel/object.cpp35
-rw-r--r--src/core/hle/kernel/object.h100
-rw-r--r--src/core/hle/kernel/object_address_table.cpp36
-rw-r--r--src/core/hle/kernel/object_address_table.h62
-rw-r--r--src/core/hle/kernel/process.cpp84
-rw-r--r--src/core/hle/kernel/process.h64
-rw-r--r--src/core/hle/kernel/resource_limit.h2
-rw-r--r--src/core/hle/kernel/scheduler.cpp4
-rw-r--r--src/core/hle/kernel/scheduler.h4
-rw-r--r--src/core/hle/kernel/server_port.cpp2
-rw-r--r--src/core/hle/kernel/server_port.h3
-rw-r--r--src/core/hle/kernel/server_session.cpp4
-rw-r--r--src/core/hle/kernel/server_session.h6
-rw-r--r--src/core/hle/kernel/session.h2
-rw-r--r--src/core/hle/kernel/shared_memory.cpp37
-rw-r--r--src/core/hle/kernel/shared_memory.h8
-rw-r--r--src/core/hle/kernel/svc.cpp6
-rw-r--r--src/core/hle/kernel/thread.cpp56
-rw-r--r--src/core/hle/kernel/thread.h15
-rw-r--r--src/core/hle/kernel/timer.cpp2
-rw-r--r--src/core/hle/kernel/timer.h2
-rw-r--r--src/core/hle/kernel/vm_manager.cpp36
-rw-r--r--src/core/hle/kernel/vm_manager.h8
-rw-r--r--src/core/hle/kernel/wait_object.cpp5
-rw-r--r--src/core/hle/kernel/wait_object.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp30
-rw-r--r--src/core/hle/service/am/am.cpp9
-rw-r--r--src/core/hle/service/am/idle.cpp24
-rw-r--r--src/core/hle/service/am/idle.h16
-rw-r--r--src/core/hle/service/am/omm.cpp42
-rw-r--r--src/core/hle/service/am/omm.h16
-rw-r--r--src/core/hle/service/am/spsm.cpp30
-rw-r--r--src/core/hle/service/am/spsm.h16
-rw-r--r--src/core/hle/service/apm/apm.cpp1
-rw-r--r--src/core/hle/service/apm/interface.cpp25
-rw-r--r--src/core/hle/service/apm/interface.h8
-rw-r--r--src/core/hle/service/arp/arp.cpp75
-rw-r--r--src/core/hle/service/arp/arp.h16
-rw-r--r--src/core/hle/service/audio/audctl.cpp45
-rw-r--r--src/core/hle/service/audio/audctl.h16
-rw-r--r--src/core/hle/service/audio/auddbg.cpp20
-rw-r--r--src/core/hle/service/audio/auddbg.h16
-rw-r--r--src/core/hle/service/audio/audin_a.cpp22
-rw-r--r--src/core/hle/service/audio/audin_a.h16
-rw-r--r--src/core/hle/service/audio/audio.cpp16
-rw-r--r--src/core/hle/service/audio/audout_a.cpp24
-rw-r--r--src/core/hle/service/audio/audout_a.h16
-rw-r--r--src/core/hle/service/audio/audout_u.cpp199
-rw-r--r--src/core/hle/service/audio/audout_u.h24
-rw-r--r--src/core/hle/service/audio/audrec_a.cpp20
-rw-r--r--src/core/hle/service/audio/audrec_a.h16
-rw-r--r--src/core/hle/service/audio/audren_a.cpp26
-rw-r--r--src/core/hle/service/audio/audren_a.h16
-rw-r--r--src/core/hle/service/audio/audren_u.cpp208
-rw-r--r--src/core/hle/service/audio/audren_u.h19
-rw-r--r--src/core/hle/service/audio/hwopus.cpp135
-rw-r--r--src/core/hle/service/audio/hwopus.h1
-rw-r--r--src/core/hle/service/bpc/bpc.cpp57
-rw-r--r--src/core/hle/service/bpc/bpc.h15
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp72
-rw-r--r--src/core/hle/service/btdrv/btdrv.h16
-rw-r--r--src/core/hle/service/btm/btm.cpp121
-rw-r--r--src/core/hle/service/btm/btm.h15
-rw-r--r--src/core/hle/service/caps/caps.cpp152
-rw-r--r--src/core/hle/service/caps/caps.h15
-rw-r--r--src/core/hle/service/fgm/fgm.cpp75
-rw-r--r--src/core/hle/service/fgm/fgm.h15
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp4
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.cpp22
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.h16
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.cpp23
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.h16
-rw-r--r--src/core/hle/service/hid/hid.cpp19
-rw-r--r--src/core/hle/service/lbl/lbl.cpp90
-rw-r--r--src/core/hle/service/lbl/lbl.h15
-rw-r--r--src/core/hle/service/lm/lm.cpp8
-rw-r--r--src/core/hle/service/mig/mig.cpp34
-rw-r--r--src/core/hle/service/mig/mig.h15
-rw-r--r--src/core/hle/service/mii/mii.cpp107
-rw-r--r--src/core/hle/service/mii/mii.h15
-rw-r--r--src/core/hle/service/ncm/ncm.cpp59
-rw-r--r--src/core/hle/service/ncm/ncm.h15
-rw-r--r--src/core/hle/service/nfc/nfc.cpp222
-rw-r--r--src/core/hle/service/nfc/nfc.h15
-rw-r--r--src/core/hle/service/ns/ns.cpp447
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp16
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp16
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp15
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp2
-rw-r--r--src/core/hle/service/nvdrv/interface.h1
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp2
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp5
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h8
-rw-r--r--src/core/hle/service/pcie/pcie.cpp64
-rw-r--r--src/core/hle/service/pcie/pcie.h15
-rw-r--r--src/core/hle/service/pcv/pcv.cpp84
-rw-r--r--src/core/hle/service/pcv/pcv.h15
-rw-r--r--src/core/hle/service/psc/psc.cpp77
-rw-r--r--src/core/hle/service/psc/psc.h15
-rw-r--r--src/core/hle/service/service.cpp36
-rw-r--r--src/core/hle/service/service.h3
-rw-r--r--src/core/hle/service/set/set.cpp15
-rw-r--r--src/core/hle/service/set/set.h2
-rw-r--r--src/core/hle/service/sm/sm.h8
-rw-r--r--src/core/hle/service/time/time.cpp4
-rw-r--r--src/core/hle/service/usb/usb.cpp238
-rw-r--r--src/core/hle/service/usb/usb.h15
-rw-r--r--src/core/hle/service/wlan/wlan.cpp172
-rw-r--r--src/core/hle/service/wlan/wlan.h15
-rw-r--r--src/core/hw/aes/ccm.cpp28
-rw-r--r--src/core/hw/hw.cpp96
-rw-r--r--src/core/hw/hw.h50
-rw-r--r--src/core/hw/lcd.cpp67
-rw-r--r--src/core/hw/lcd.h86
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp11
-rw-r--r--src/core/loader/deconstructed_rom_directory.h6
-rw-r--r--src/core/loader/elf.cpp6
-rw-r--r--src/core/loader/loader.cpp9
-rw-r--r--src/core/loader/loader.h6
-rw-r--r--src/core/loader/nca.cpp63
-rw-r--r--src/core/loader/nca.h6
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/loader/nso.h2
-rw-r--r--src/core/loader/xci.cpp74
-rw-r--r--src/core/loader/xci.h44
-rw-r--r--src/core/memory.cpp146
-rw-r--r--src/core/memory.h111
-rw-r--r--src/core/perf_stats.cpp17
-rw-r--r--src/core/perf_stats.h8
-rw-r--r--src/core/settings.cpp10
-rw-r--r--src/core/settings.h9
-rw-r--r--src/input_common/keyboard.cpp5
-rw-r--r--src/input_common/motion_emu.cpp2
-rw-r--r--src/input_common/sdl/sdl.cpp20
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/core/memory/memory.cpp56
-rw-r--r--src/video_core/engines/maxwell_3d.cpp12
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/gpu.cpp6
-rw-r--r--src/video_core/gpu.h7
-rw-r--r--src/video_core/macro_interpreter.cpp4
-rw-r--r--src/video_core/macro_interpreter.h4
-rw-r--r--src/video_core/renderer_base.cpp18
-rw-r--r--src/video_core/renderer_base.h29
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h36
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h6
-rw-r--r--src/video_core/renderer_opengl/gl_state.h5
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp30
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h17
-rw-r--r--src/video_core/textures/decoders.cpp4
-rw-r--r--src/video_core/video_core.cpp28
-rw-r--r--src/video_core/video_core.h24
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/about_dialog.cpp7
-rw-r--r--src/yuzu/about_dialog.h2
-rw-r--r--src/yuzu/aboutdialog.ui4
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/configuration/config.cpp19
-rw-r--r--src/yuzu/configuration/configure.ui21
-rw-r--r--src/yuzu/configuration/configure_audio.cpp90
-rw-r--r--src/yuzu/configuration/configure_audio.h31
-rw-r--r--src/yuzu/configuration/configure_audio.ui130
-rw-r--r--src/yuzu/configuration/configure_debug.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp8
-rw-r--r--src/yuzu/configuration/configure_dialog.h4
-rw-r--r--src/yuzu/configuration/configure_general.cpp7
-rw-r--r--src/yuzu/configuration/configure_general.h4
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp3
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp10
-rw-r--r--src/yuzu/configuration/configure_system.ui29
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp6
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoint_observer.h8
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp26
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.h11
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints_p.h1
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp37
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h15
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.h6
-rw-r--r--src/yuzu/game_list.cpp46
-rw-r--r--src/yuzu/game_list_p.h17
-rw-r--r--src/yuzu/hotkeys.cpp67
-rw-r--r--src/yuzu/hotkeys.h107
-rw-r--r--src/yuzu/main.cpp173
-rw-r--r--src/yuzu/main.h5
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/default_ini.h22
-rw-r--r--src/yuzu_cmd/yuzu.cpp20
290 files changed, 8073 insertions, 2424 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 85354f43e..a88551fbc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,6 +3,7 @@ include_directories(.)
3 3
4add_subdirectory(common) 4add_subdirectory(common)
5add_subdirectory(core) 5add_subdirectory(core)
6add_subdirectory(audio_core)
6add_subdirectory(video_core) 7add_subdirectory(video_core)
7add_subdirectory(input_common) 8add_subdirectory(input_common)
8add_subdirectory(tests) 9add_subdirectory(tests)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
new file mode 100644
index 000000000..ec71524a3
--- /dev/null
+++ b/src/audio_core/CMakeLists.txt
@@ -0,0 +1,27 @@
1add_library(audio_core STATIC
2 audio_out.cpp
3 audio_out.h
4 audio_renderer.cpp
5 audio_renderer.h
6 buffer.h
7 codec.cpp
8 codec.h
9 null_sink.h
10 stream.cpp
11 stream.h
12 sink.h
13 sink_details.cpp
14 sink_details.h
15 sink_stream.h
16
17 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
18)
19
20create_target_directory_groups(audio_core)
21
22target_link_libraries(audio_core PUBLIC common core)
23
24if(ENABLE_CUBEB)
25 target_link_libraries(audio_core PRIVATE cubeb)
26 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
27endif()
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
new file mode 100644
index 000000000..12632a95c
--- /dev/null
+++ b/src/audio_core/audio_out.cpp
@@ -0,0 +1,58 @@
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 "audio_core/audio_out.h"
6#include "audio_core/sink.h"
7#include "audio_core/sink_details.h"
8#include "common/assert.h"
9#include "common/logging/log.h"
10#include "core/settings.h"
11
12namespace AudioCore {
13
14/// Returns the stream format from the specified number of channels
15static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
16 switch (num_channels) {
17 case 1:
18 return Stream::Format::Mono16;
19 case 2:
20 return Stream::Format::Stereo16;
21 case 6:
22 return Stream::Format::Multi51Channel16;
23 }
24
25 LOG_CRITICAL(Audio, "Unimplemented num_channels={}", num_channels);
26 UNREACHABLE();
27 return {};
28}
29
30StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
31 Stream::ReleaseCallback&& release_callback) {
32 if (!sink) {
33 const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
34 sink = sink_details.factory(Settings::values.audio_device_id);
35 }
36
37 return std::make_shared<Stream>(
38 sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
39 sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
40}
41
42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
43 return stream->GetTagsAndReleaseBuffers(max_count);
44}
45
46void AudioOut::StartStream(StreamPtr stream) {
47 stream->Play();
48}
49
50void AudioOut::StopStream(StreamPtr stream) {
51 stream->Stop();
52}
53
54bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
55 return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
56}
57
58} // namespace AudioCore
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
new file mode 100644
index 000000000..39b7e656b
--- /dev/null
+++ b/src/audio_core/audio_out.h
@@ -0,0 +1,43 @@
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 <memory>
8#include <string>
9#include <vector>
10
11#include "audio_core/buffer.h"
12#include "audio_core/sink.h"
13#include "audio_core/stream.h"
14#include "common/common_types.h"
15
16namespace AudioCore {
17
18/**
19 * Represents an audio playback interface, used to open and play audio streams
20 */
21class AudioOut {
22public:
23 /// Opens a new audio stream
24 StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
25 Stream::ReleaseCallback&& release_callback);
26
27 /// Returns a vector of recently released buffers specified by tag for the specified stream
28 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
29
30 /// Starts an audio stream for playback
31 void StartStream(StreamPtr stream);
32
33 /// Stops an audio stream that is currently playing
34 void StopStream(StreamPtr stream);
35
36 /// Queues a buffer into the specified audio stream, returns true on success
37 bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
38
39private:
40 SinkPtr sink;
41};
42
43} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
new file mode 100644
index 000000000..282f345c5
--- /dev/null
+++ b/src/audio_core/audio_renderer.cpp
@@ -0,0 +1,234 @@
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 "audio_core/audio_renderer.h"
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/memory.h"
9
10namespace AudioCore {
11
12constexpr u32 STREAM_SAMPLE_RATE{48000};
13constexpr u32 STREAM_NUM_CHANNELS{2};
14
15AudioRenderer::AudioRenderer(AudioRendererParameter params,
16 Kernel::SharedPtr<Kernel::Event> buffer_event)
17 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
18
19 audio_core = std::make_unique<AudioCore::AudioOut>();
20 stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
21 [=]() { buffer_event->Signal(); });
22 audio_core->StartStream(stream);
23
24 QueueMixedBuffer(0);
25 QueueMixedBuffer(1);
26 QueueMixedBuffer(2);
27}
28
29std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
30 // Copy UpdateDataHeader struct
31 UpdateDataHeader config{};
32 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
33 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
34
35 // Copy MemoryPoolInfo structs
36 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
37 std::memcpy(mem_pool_info.data(),
38 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
39 memory_pool_count * sizeof(MemoryPoolInfo));
40
41 // Copy VoiceInfo structs
42 size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
43 config.voice_resource_size};
44 for (auto& voice : voices) {
45 std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
46 offset += sizeof(VoiceInfo);
47 }
48
49 // Update voices
50 for (auto& voice : voices) {
51 voice.UpdateState();
52 if (!voice.GetInfo().is_in_use) {
53 continue;
54 }
55 if (voice.GetInfo().is_new) {
56 voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
57 }
58 }
59
60 // Update memory pool state
61 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
62 for (size_t index = 0; index < memory_pool.size(); ++index) {
63 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
64 memory_pool[index].state = MemoryPoolStates::Attached;
65 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
66 memory_pool[index].state = MemoryPoolStates::Detached;
67 }
68 }
69
70 // Release previous buffers and queue next ones for playback
71 ReleaseAndQueueBuffers();
72
73 // Copy output header
74 UpdateDataHeader response_data{worker_params};
75 std::vector<u8> output_params(response_data.total_size);
76 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
77
78 // Copy output memory pool entries
79 std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
80 response_data.memory_pools_size);
81
82 // Copy output voice status
83 size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
84 for (const auto& voice : voices) {
85 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
86 sizeof(VoiceOutStatus));
87 voice_out_status_offset += sizeof(VoiceOutStatus);
88 }
89
90 return output_params;
91}
92
93void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
94 wave_index = index & 3;
95 is_refresh_pending = true;
96}
97
98std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
99 if (!IsPlaying()) {
100 return {};
101 }
102
103 if (is_refresh_pending) {
104 RefreshBuffer();
105 }
106
107 const size_t max_size{samples.size() - offset};
108 const size_t dequeue_offset{offset};
109 size_t size{sample_count * STREAM_NUM_CHANNELS};
110 if (size > max_size) {
111 size = max_size;
112 }
113
114 out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
115 offset += size;
116
117 const auto& wave_buffer{info.wave_buffer[wave_index]};
118 if (offset == samples.size()) {
119 offset = 0;
120
121 if (!wave_buffer.is_looping) {
122 SetWaveIndex(wave_index + 1);
123 }
124
125 out_status.wave_buffer_consumed++;
126
127 if (wave_buffer.end_of_stream) {
128 info.play_state = PlayState::Paused;
129 }
130 }
131
132 return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
133}
134
135void AudioRenderer::VoiceState::UpdateState() {
136 if (is_in_use && !info.is_in_use) {
137 // No longer in use, reset state
138 is_refresh_pending = true;
139 wave_index = 0;
140 offset = 0;
141 out_status = {};
142 }
143 is_in_use = info.is_in_use;
144}
145
146void AudioRenderer::VoiceState::RefreshBuffer() {
147 std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
148 Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
149 info.wave_buffer[wave_index].buffer_sz);
150
151 switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
152 case Codec::PcmFormat::Int16: {
153 // PCM16 is played as-is
154 break;
155 }
156 case Codec::PcmFormat::Adpcm: {
157 // Decode ADPCM to PCM16
158 Codec::ADPCM_Coeff coeffs;
159 Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
160 new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
161 new_samples.size() * sizeof(s16), coeffs, adpcm_state);
162 break;
163 }
164 default:
165 LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
166 UNREACHABLE();
167 break;
168 }
169
170 switch (info.channel_count) {
171 case 1:
172 // 1 channel is upsampled to 2 channel
173 samples.resize(new_samples.size() * 2);
174 for (size_t index = 0; index < new_samples.size(); ++index) {
175 samples[index * 2] = new_samples[index];
176 samples[index * 2 + 1] = new_samples[index];
177 }
178 break;
179 case 2: {
180 // 2 channel is played as is
181 samples = std::move(new_samples);
182 break;
183 }
184 default:
185 LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
186 UNREACHABLE();
187 break;
188 }
189
190 is_refresh_pending = false;
191}
192
193static constexpr s16 ClampToS16(s32 value) {
194 return static_cast<s16>(std::clamp(value, -32768, 32767));
195}
196
197void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
198 constexpr size_t BUFFER_SIZE{512};
199 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
200
201 for (auto& voice : voices) {
202 if (!voice.IsPlaying()) {
203 continue;
204 }
205
206 size_t offset{};
207 s64 samples_remaining{BUFFER_SIZE};
208 while (samples_remaining > 0) {
209 const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
210
211 if (samples.empty()) {
212 break;
213 }
214
215 samples_remaining -= samples.size();
216
217 for (const auto& sample : samples) {
218 const s32 buffer_sample{buffer[offset]};
219 buffer[offset++] =
220 ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
221 }
222 }
223 }
224 audio_core->QueueBuffer(stream, tag, std::move(buffer));
225}
226
227void AudioRenderer::ReleaseAndQueueBuffers() {
228 const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
229 for (const auto& tag : released_buffers) {
230 QueueMixedBuffer(tag);
231 }
232}
233
234} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
new file mode 100644
index 000000000..6950a4681
--- /dev/null
+++ b/src/audio_core/audio_renderer.h
@@ -0,0 +1,206 @@
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 <array>
8#include <memory>
9#include <vector>
10
11#include "audio_core/audio_out.h"
12#include "audio_core/codec.h"
13#include "audio_core/stream.h"
14#include "common/common_types.h"
15#include "common/swap.h"
16#include "core/hle/kernel/event.h"
17
18namespace AudioCore {
19
20enum class PlayState : u8 {
21 Started = 0,
22 Stopped = 1,
23 Paused = 2,
24};
25
26struct AudioRendererParameter {
27 u32_le sample_rate;
28 u32_le sample_count;
29 u32_le unknown_8;
30 u32_le unknown_c;
31 u32_le voice_count;
32 u32_le sink_count;
33 u32_le effect_count;
34 u32_le unknown_1c;
35 u8 unknown_20;
36 INSERT_PADDING_BYTES(3);
37 u32_le splitter_count;
38 u32_le unknown_2c;
39 INSERT_PADDING_WORDS(1);
40 u32_le revision;
41};
42static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
43
44enum class MemoryPoolStates : u32 { // Should be LE
45 Invalid = 0x0,
46 Unknown = 0x1,
47 RequestDetach = 0x2,
48 Detached = 0x3,
49 RequestAttach = 0x4,
50 Attached = 0x5,
51 Released = 0x6,
52};
53
54struct MemoryPoolEntry {
55 MemoryPoolStates state;
56 u32_le unknown_4;
57 u32_le unknown_8;
58 u32_le unknown_c;
59};
60static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
61
62struct MemoryPoolInfo {
63 u64_le pool_address;
64 u64_le pool_size;
65 MemoryPoolStates pool_state;
66 INSERT_PADDING_WORDS(3); // Unknown
67};
68static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
69struct BiquadFilter {
70 u8 enable;
71 INSERT_PADDING_BYTES(1);
72 std::array<s16_le, 3> numerator;
73 std::array<s16_le, 2> denominator;
74};
75static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
76
77struct WaveBuffer {
78 u64_le buffer_addr;
79 u64_le buffer_sz;
80 s32_le start_sample_offset;
81 s32_le end_sample_offset;
82 u8 is_looping;
83 u8 end_of_stream;
84 u8 sent_to_server;
85 INSERT_PADDING_BYTES(5);
86 u64 context_addr;
87 u64 context_sz;
88 INSERT_PADDING_BYTES(8);
89};
90static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
91
92struct VoiceInfo {
93 u32_le id;
94 u32_le node_id;
95 u8 is_new;
96 u8 is_in_use;
97 PlayState play_state;
98 u8 sample_format;
99 u32_le sample_rate;
100 u32_le priority;
101 u32_le sorting_order;
102 u32_le channel_count;
103 float_le pitch;
104 float_le volume;
105 std::array<BiquadFilter, 2> biquad_filter;
106 u32_le wave_buffer_count;
107 u32_le wave_buffer_head;
108 INSERT_PADDING_WORDS(1);
109 u64_le additional_params_addr;
110 u64_le additional_params_sz;
111 u32_le mix_id;
112 u32_le splitter_info_id;
113 std::array<WaveBuffer, 4> wave_buffer;
114 std::array<u32_le, 6> voice_channel_resource_ids;
115 INSERT_PADDING_BYTES(24);
116};
117static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
118
119struct VoiceOutStatus {
120 u64_le played_sample_count;
121 u32_le wave_buffer_consumed;
122 u32_le voice_drops_count;
123};
124static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
125
126struct UpdateDataHeader {
127 UpdateDataHeader() {}
128
129 explicit UpdateDataHeader(const AudioRendererParameter& config) {
130 revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
131 behavior_size = 0xb0;
132 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
133 voices_size = config.voice_count * 0x10;
134 voice_resource_size = 0x0;
135 effects_size = config.effect_count * 0x10;
136 mixes_size = 0x0;
137 sinks_size = config.sink_count * 0x20;
138 performance_manager_size = 0x10;
139 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
140 effects_size + sinks_size + performance_manager_size;
141 }
142
143 u32_le revision;
144 u32_le behavior_size;
145 u32_le memory_pools_size;
146 u32_le voices_size;
147 u32_le voice_resource_size;
148 u32_le effects_size;
149 u32_le mixes_size;
150 u32_le sinks_size;
151 u32_le performance_manager_size;
152 INSERT_PADDING_WORDS(6);
153 u32_le total_size;
154};
155static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
156
157class AudioRenderer {
158public:
159 AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
160 std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
161 void QueueMixedBuffer(Buffer::Tag tag);
162 void ReleaseAndQueueBuffers();
163
164private:
165 class VoiceState {
166 public:
167 bool IsPlaying() const {
168 return is_in_use && info.play_state == PlayState::Started;
169 }
170
171 const VoiceOutStatus& GetOutStatus() const {
172 return out_status;
173 }
174
175 const VoiceInfo& GetInfo() const {
176 return info;
177 }
178
179 VoiceInfo& Info() {
180 return info;
181 }
182
183 void SetWaveIndex(size_t index);
184 std::vector<s16> DequeueSamples(size_t sample_count);
185 void UpdateState();
186 void RefreshBuffer();
187
188 private:
189 bool is_in_use{};
190 bool is_refresh_pending{};
191 size_t wave_index{};
192 size_t offset{};
193 Codec::ADPCMState adpcm_state{};
194 std::vector<s16> samples;
195 VoiceOutStatus out_status{};
196 VoiceInfo info{};
197 };
198
199 AudioRendererParameter worker_params;
200 Kernel::SharedPtr<Kernel::Event> buffer_event;
201 std::vector<VoiceState> voices;
202 std::unique_ptr<AudioCore::AudioOut> audio_core;
203 AudioCore::StreamPtr stream;
204};
205
206} // namespace AudioCore
diff --git a/src/audio_core/buffer.h b/src/audio_core/buffer.h
new file mode 100644
index 000000000..a323b23ec
--- /dev/null
+++ b/src/audio_core/buffer.h
@@ -0,0 +1,45 @@
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 <memory>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace AudioCore {
13
14/**
15 * Represents a buffer of audio samples to be played in an audio stream
16 */
17class Buffer {
18public:
19 using Tag = u64;
20
21 Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
22
23 /// Returns the raw audio data for the buffer
24 std::vector<s16>& Samples() {
25 return samples;
26 }
27
28 /// Returns the raw audio data for the buffer
29 const std::vector<s16>& GetSamples() const {
30 return samples;
31 }
32
33 /// Returns the buffer tag, this is provided by the game to the audout service
34 Tag GetTag() const {
35 return tag;
36 }
37
38private:
39 Tag tag;
40 std::vector<s16> samples;
41};
42
43using BufferPtr = std::shared_ptr<Buffer>;
44
45} // namespace AudioCore
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
new file mode 100644
index 000000000..c3021403f
--- /dev/null
+++ b/src/audio_core/codec.cpp
@@ -0,0 +1,77 @@
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 <algorithm>
6
7#include "audio_core/codec.h"
8
9namespace AudioCore::Codec {
10
11std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
12 ADPCMState& state) {
13 // GC-ADPCM with scale factor and variable coefficients.
14 // Frames are 8 bytes long containing 14 samples each.
15 // Samples are 4 bits (one nibble) long.
16
17 constexpr size_t FRAME_LEN = 8;
18 constexpr size_t SAMPLES_PER_FRAME = 14;
19 constexpr std::array<int, 16> SIGNED_NIBBLES = {
20 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
21
22 const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
23 const size_t ret_size =
24 sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
25 std::vector<s16> ret(ret_size);
26
27 int yn1 = state.yn1, yn2 = state.yn2;
28
29 const size_t NUM_FRAMES =
30 (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
31 for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
32 const int frame_header = data[framei * FRAME_LEN];
33 const int scale = 1 << (frame_header & 0xF);
34 const int idx = (frame_header >> 4) & 0x7;
35
36 // Coefficients are fixed point with 11 bits fractional part.
37 const int coef1 = coeff[idx * 2 + 0];
38 const int coef2 = coeff[idx * 2 + 1];
39
40 // Decodes an audio sample. One nibble produces one sample.
41 const auto decode_sample = [&](const int nibble) -> s16 {
42 const int xn = nibble * scale;
43 // We first transform everything into 11 bit fixed point, perform the second order
44 // digital filter, then transform back.
45 // 0x400 == 0.5 in 11 bit fixed point.
46 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
47 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
48 // Clamp to output range.
49 val = std::clamp<s32>(val, -32768, 32767);
50 // Advance output feedback.
51 yn2 = yn1;
52 yn1 = val;
53 return static_cast<s16>(val);
54 };
55
56 size_t outputi = framei * SAMPLES_PER_FRAME;
57 size_t datai = framei * FRAME_LEN + 1;
58 for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
59 const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
60 ret[outputi] = sample1;
61 outputi++;
62
63 const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
64 ret[outputi] = sample2;
65 outputi++;
66
67 datai++;
68 }
69 }
70
71 state.yn1 = yn1;
72 state.yn2 = yn2;
73
74 return ret;
75}
76
77} // namespace AudioCore::Codec
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
new file mode 100644
index 000000000..3f845c42c
--- /dev/null
+++ b/src/audio_core/codec.h
@@ -0,0 +1,44 @@
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 <array>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace AudioCore::Codec {
13
14enum class PcmFormat : u32 {
15 Invalid = 0,
16 Int8 = 1,
17 Int16 = 2,
18 Int24 = 3,
19 Int32 = 4,
20 PcmFloat = 5,
21 Adpcm = 6,
22};
23
24/// See: Codec::DecodeADPCM
25struct ADPCMState {
26 // Two historical samples from previous processed buffer,
27 // required for ADPCM decoding
28 s16 yn1; ///< y[n-1]
29 s16 yn2; ///< y[n-2]
30};
31
32using ADPCM_Coeff = std::array<s16, 16>;
33
34/**
35 * @param data Pointer to buffer that contains ADPCM data to decode
36 * @param size Size of buffer in bytes
37 * @param coeff ADPCM coefficients
38 * @param state ADPCM state, this is updated with new state
39 * @return Decoded stereo signed PCM16 data, sample_count in length
40 */
41std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
42 ADPCMState& state);
43
44}; // namespace AudioCore::Codec
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
new file mode 100644
index 000000000..1501ef1f4
--- /dev/null
+++ b/src/audio_core/cubeb_sink.cpp
@@ -0,0 +1,200 @@
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 <algorithm>
6#include <cstring>
7
8#include "audio_core/cubeb_sink.h"
9#include "audio_core/stream.h"
10#include "common/logging/log.h"
11
12namespace AudioCore {
13
14class SinkStreamImpl final : public SinkStream {
15public:
16 SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
17 const std::string& name)
18 : ctx{ctx}, num_channels{num_channels_} {
19
20 if (num_channels == 6) {
21 // 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
22 // channel for now
23 is_6_channel = true;
24 num_channels = 2;
25 }
26
27 cubeb_stream_params params{};
28 params.rate = sample_rate;
29 params.channels = num_channels;
30 params.format = CUBEB_SAMPLE_S16NE;
31 params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
32
33 u32 minimum_latency{};
34 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
35 LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
36 }
37
38 if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
39 &params, std::max(512u, minimum_latency),
40 &SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
41 this) != CUBEB_OK) {
42 LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
43 return;
44 }
45
46 if (cubeb_stream_start(stream_backend) != CUBEB_OK) {
47 LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream");
48 return;
49 }
50 }
51
52 ~SinkStreamImpl() {
53 if (!ctx) {
54 return;
55 }
56
57 if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
58 LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
59 }
60
61 cubeb_stream_destroy(stream_backend);
62 }
63
64 void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
65 if (!ctx) {
66 return;
67 }
68
69 queue.reserve(queue.size() + samples.size() * GetNumChannels());
70
71 if (is_6_channel) {
72 // Downsample 6 channels to 2
73 const size_t sample_count_copy_size = samples.size() * 2;
74 queue.reserve(sample_count_copy_size);
75 for (size_t i = 0; i < samples.size(); i += num_channels) {
76 queue.push_back(samples[i]);
77 queue.push_back(samples[i + 1]);
78 }
79 } else {
80 // Copy as-is
81 std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
82 }
83 }
84
85 u32 GetNumChannels() const {
86 return num_channels;
87 }
88
89private:
90 std::vector<std::string> device_list;
91
92 cubeb* ctx{};
93 cubeb_stream* stream_backend{};
94 u32 num_channels{};
95 bool is_6_channel{};
96
97 std::vector<s16> queue;
98
99 static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
100 void* output_buffer, long num_frames);
101 static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state);
102};
103
104CubebSink::CubebSink(std::string target_device_name) {
105 if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) {
106 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
107 return;
108 }
109
110 if (target_device_name != auto_device_name && !target_device_name.empty()) {
111 cubeb_device_collection collection;
112 if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
113 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
114 } else {
115 const auto collection_end{collection.device + collection.count};
116 const auto device{std::find_if(collection.device, collection_end,
117 [&](const cubeb_device_info& device) {
118 return target_device_name == device.friendly_name;
119 })};
120 if (device != collection_end) {
121 output_device = device->devid;
122 }
123 cubeb_device_collection_destroy(ctx, &collection);
124 }
125 }
126}
127
128CubebSink::~CubebSink() {
129 if (!ctx) {
130 return;
131 }
132
133 for (auto& sink_stream : sink_streams) {
134 sink_stream.reset();
135 }
136
137 cubeb_destroy(ctx);
138}
139
140SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
141 const std::string& name) {
142 sink_streams.push_back(
143 std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
144 return *sink_streams.back();
145}
146
147long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
148 void* output_buffer, long num_frames) {
149 SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data);
150 u8* buffer = reinterpret_cast<u8*>(output_buffer);
151
152 if (!impl) {
153 return {};
154 }
155
156 const size_t frames_to_write{
157 std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))};
158
159 memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels());
160 impl->queue.erase(impl->queue.begin(),
161 impl->queue.begin() + frames_to_write * impl->GetNumChannels());
162
163 if (frames_to_write < num_frames) {
164 // Fill the rest of the frames with silence
165 memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0,
166 (num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels());
167 }
168
169 return num_frames;
170}
171
172void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
173
174std::vector<std::string> ListCubebSinkDevices() {
175 std::vector<std::string> device_list;
176 cubeb* ctx;
177
178 if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) {
179 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
180 return {};
181 }
182
183 cubeb_device_collection collection;
184 if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
185 LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
186 } else {
187 for (size_t i = 0; i < collection.count; i++) {
188 const cubeb_device_info& device = collection.device[i];
189 if (device.friendly_name) {
190 device_list.emplace_back(device.friendly_name);
191 }
192 }
193 cubeb_device_collection_destroy(ctx, &collection);
194 }
195
196 cubeb_destroy(ctx);
197 return device_list;
198}
199
200} // namespace AudioCore
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h
new file mode 100644
index 000000000..59cbf05e9
--- /dev/null
+++ b/src/audio_core/cubeb_sink.h
@@ -0,0 +1,32 @@
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 <string>
8#include <vector>
9
10#include <cubeb/cubeb.h>
11
12#include "audio_core/sink.h"
13
14namespace AudioCore {
15
16class CubebSink final : public Sink {
17public:
18 explicit CubebSink(std::string device_id);
19 ~CubebSink() override;
20
21 SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
22 const std::string& name) override;
23
24private:
25 cubeb* ctx{};
26 cubeb_devid output_device{};
27 std::vector<SinkStreamPtr> sink_streams;
28};
29
30std::vector<std::string> ListCubebSinkDevices();
31
32} // namespace AudioCore
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
new file mode 100644
index 000000000..f235d93e5
--- /dev/null
+++ b/src/audio_core/null_sink.h
@@ -0,0 +1,27 @@
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 "audio_core/sink.h"
8
9namespace AudioCore {
10
11class NullSink final : public Sink {
12public:
13 explicit NullSink(std::string){};
14 ~NullSink() override = default;
15
16 SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
17 const std::string& /*name*/) override {
18 return null_sink_stream;
19 }
20
21private:
22 struct NullSinkStreamImpl final : SinkStream {
23 void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
24 } null_sink_stream;
25};
26
27} // namespace AudioCore
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
new file mode 100644
index 000000000..95c7b2b6e
--- /dev/null
+++ b/src/audio_core/sink.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 <memory>
8#include <string>
9
10#include "audio_core/sink_stream.h"
11#include "common/common_types.h"
12
13namespace AudioCore {
14
15constexpr char auto_device_name[] = "auto";
16
17/**
18 * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
19 * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate.
20 * They are dumb outputs.
21 */
22class Sink {
23public:
24 virtual ~Sink() = default;
25 virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
26 const std::string& name) = 0;
27};
28
29using SinkPtr = std::unique_ptr<Sink>;
30
31} // namespace AudioCore
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
new file mode 100644
index 000000000..955ba20fb
--- /dev/null
+++ b/src/audio_core/sink_details.cpp
@@ -0,0 +1,44 @@
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 <algorithm>
6#include <memory>
7#include <string>
8#include <vector>
9#include "audio_core/null_sink.h"
10#include "audio_core/sink_details.h"
11#ifdef HAVE_CUBEB
12#include "audio_core/cubeb_sink.h"
13#endif
14#include "common/logging/log.h"
15
16namespace AudioCore {
17
18// g_sink_details is ordered in terms of desirability, with the best choice at the top.
19const std::vector<SinkDetails> g_sink_details = {
20#ifdef HAVE_CUBEB
21 SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices},
22#endif
23 SinkDetails{"null", &std::make_unique<NullSink, std::string>,
24 [] { return std::vector<std::string>{"null"}; }},
25};
26
27const SinkDetails& GetSinkDetails(std::string sink_id) {
28 auto iter =
29 std::find_if(g_sink_details.begin(), g_sink_details.end(),
30 [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
31
32 if (sink_id == "auto" || iter == g_sink_details.end()) {
33 if (sink_id != "auto") {
34 LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id);
35 }
36 // Auto-select.
37 // g_sink_details is ordered in terms of desirability, with the best choice at the front.
38 iter = g_sink_details.begin();
39 }
40
41 return *iter;
42}
43
44} // namespace AudioCore
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h
new file mode 100644
index 000000000..ea666c554
--- /dev/null
+++ b/src/audio_core/sink_details.h
@@ -0,0 +1,35 @@
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 <functional>
8#include <memory>
9#include <utility>
10#include <vector>
11
12namespace AudioCore {
13
14class Sink;
15
16struct SinkDetails {
17 using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>;
18 using ListDevicesFn = std::function<std::vector<std::string>()>;
19
20 SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_)
21 : id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {}
22
23 /// Name for this sink.
24 const char* id;
25 /// A method to call to construct an instance of this type of sink.
26 FactoryFn factory;
27 /// A method to call to list available devices.
28 ListDevicesFn list_devices;
29};
30
31extern const std::vector<SinkDetails> g_sink_details;
32
33const SinkDetails& GetSinkDetails(std::string sink_id);
34
35} // namespace AudioCore
diff --git a/src/audio_core/sink_stream.h b/src/audio_core/sink_stream.h
new file mode 100644
index 000000000..41b6736d8
--- /dev/null
+++ b/src/audio_core/sink_stream.h
@@ -0,0 +1,32 @@
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 <memory>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace AudioCore {
13
14/**
15 * Accepts samples in stereo signed PCM16 format to be output. Sinks *do not* handle resampling and
16 * expect the correct sample rate. They are dumb outputs.
17 */
18class SinkStream {
19public:
20 virtual ~SinkStream() = default;
21
22 /**
23 * Feed stereo samples to sink.
24 * @param num_channels Number of channels used.
25 * @param samples Samples in interleaved stereo PCM16 format.
26 */
27 virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
28};
29
30using SinkStreamPtr = std::unique_ptr<SinkStream>;
31
32} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
new file mode 100644
index 000000000..ad9e2915c
--- /dev/null
+++ b/src/audio_core/stream.cpp
@@ -0,0 +1,127 @@
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 <algorithm>
6#include <cmath>
7
8#include "audio_core/sink.h"
9#include "audio_core/sink_details.h"
10#include "audio_core/stream.h"
11#include "common/assert.h"
12#include "common/logging/log.h"
13#include "core/core_timing.h"
14#include "core/core_timing_util.h"
15#include "core/settings.h"
16
17namespace AudioCore {
18
19constexpr size_t MaxAudioBufferCount{32};
20
21u32 Stream::GetNumChannels() const {
22 switch (format) {
23 case Format::Mono16:
24 return 1;
25 case Format::Stereo16:
26 return 2;
27 case Format::Multi51Channel16:
28 return 6;
29 }
30 LOG_CRITICAL(Audio, "Unimplemented format={}", static_cast<u32>(format));
31 UNREACHABLE();
32 return {};
33}
34
35Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
36 SinkStream& sink_stream, std::string&& name_)
37 : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
38 sink_stream{sink_stream}, name{std::move(name_)} {
39
40 release_event = CoreTiming::RegisterEvent(
41 name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
42}
43
44void Stream::Play() {
45 state = State::Playing;
46 PlayNextBuffer();
47}
48
49void Stream::Stop() {
50 ASSERT_MSG(false, "Unimplemented");
51}
52
53s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
54 const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
55 return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
56}
57
58static void VolumeAdjustSamples(std::vector<s16>& samples) {
59 const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
60
61 if (volume == 1.0f) {
62 return;
63 }
64
65 // Implementation of a volume slider with a dynamic range of 60 dB
66 const float volume_scale_factor{std::exp(6.90775f * volume) * 0.001f};
67 for (auto& sample : samples) {
68 sample = static_cast<s16>(sample * volume_scale_factor);
69 }
70}
71
72void Stream::PlayNextBuffer() {
73 if (!IsPlaying()) {
74 // Ensure we are in playing state before playing the next buffer
75 return;
76 }
77
78 if (active_buffer) {
79 // Do not queue a new buffer if we are already playing a buffer
80 return;
81 }
82
83 if (queued_buffers.empty()) {
84 // No queued buffers - we are effectively paused
85 return;
86 }
87
88 active_buffer = queued_buffers.front();
89 queued_buffers.pop();
90
91 VolumeAdjustSamples(active_buffer->Samples());
92 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
93
94 CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
95}
96
97void Stream::ReleaseActiveBuffer() {
98 ASSERT(active_buffer);
99 released_buffers.push(std::move(active_buffer));
100 release_callback();
101 PlayNextBuffer();
102}
103
104bool Stream::QueueBuffer(BufferPtr&& buffer) {
105 if (queued_buffers.size() < MaxAudioBufferCount) {
106 queued_buffers.push(std::move(buffer));
107 PlayNextBuffer();
108 return true;
109 }
110 return false;
111}
112
113bool Stream::ContainsBuffer(Buffer::Tag tag) const {
114 ASSERT_MSG(false, "Unimplemented");
115 return {};
116}
117
118std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
119 std::vector<Buffer::Tag> tags;
120 for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
121 tags.push_back(released_buffers.front()->GetTag());
122 released_buffers.pop();
123 }
124 return tags;
125}
126
127} // namespace AudioCore
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
new file mode 100644
index 000000000..049b92ca9
--- /dev/null
+++ b/src/audio_core/stream.h
@@ -0,0 +1,102 @@
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 <functional>
8#include <memory>
9#include <string>
10#include <vector>
11#include <queue>
12
13#include "audio_core/buffer.h"
14#include "audio_core/sink_stream.h"
15#include "common/assert.h"
16#include "common/common_types.h"
17#include "core/core_timing.h"
18
19namespace AudioCore {
20
21/**
22 * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
23 */
24class Stream {
25public:
26 /// Audio format of the stream
27 enum class Format {
28 Mono16,
29 Stereo16,
30 Multi51Channel16,
31 };
32
33 /// Callback function type, used to change guest state on a buffer being released
34 using ReleaseCallback = std::function<void()>;
35
36 Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
37 SinkStream& sink_stream, std::string&& name_);
38
39 /// Plays the audio stream
40 void Play();
41
42 /// Stops the audio stream
43 void Stop();
44
45 /// Queues a buffer into the audio stream, returns true on success
46 bool QueueBuffer(BufferPtr&& buffer);
47
48 /// Returns true if the audio stream contains a buffer with the specified tag
49 bool ContainsBuffer(Buffer::Tag tag) const;
50
51 /// Returns a vector of recently released buffers specified by tag
52 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
53
54 /// Returns true if the stream is currently playing
55 bool IsPlaying() const {
56 return state == State::Playing;
57 }
58
59 /// Returns the number of queued buffers
60 size_t GetQueueSize() const {
61 return queued_buffers.size();
62 }
63
64 /// Gets the sample rate
65 u32 GetSampleRate() const {
66 return sample_rate;
67 }
68
69 /// Gets the number of channels
70 u32 GetNumChannels() const;
71
72private:
73 /// Current state of the stream
74 enum class State {
75 Stopped,
76 Playing,
77 };
78
79 /// Plays the next queued buffer in the audio stream, starting playback if necessary
80 void PlayNextBuffer();
81
82 /// Releases the actively playing buffer, signalling that it has been completed
83 void ReleaseActiveBuffer();
84
85 /// Gets the number of core cycles when the specified buffer will be released
86 s64 GetBufferReleaseCycles(const Buffer& buffer) const;
87
88 u32 sample_rate; ///< Sample rate of the stream
89 Format format; ///< Format of the stream
90 ReleaseCallback release_callback; ///< Buffer release callback for the stream
91 State state{State::Stopped}; ///< Playback state of the stream
92 CoreTiming::EventType* release_event{}; ///< Core timing release event for the stream
93 BufferPtr active_buffer; ///< Actively playing buffer in the stream
94 std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
95 std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
96 SinkStream& sink_stream; ///< Output sink for the stream
97 std::string name; ///< Name of the stream, must be unique
98};
99
100using StreamPtr = std::shared_ptr<Stream>;
101
102} // namespace AudioCore
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 93f1c0044..8b0d34da6 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -6,7 +6,7 @@
6 6
7#include <string> 7#include <string>
8 8
9#if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM) 9#if !defined(ARCHITECTURE_x86_64)
10#include <cstdlib> // for exit 10#include <cstdlib> // for exit
11#endif 11#endif
12#include "common/common_types.h" 12#include "common/common_types.h"
@@ -32,8 +32,6 @@
32 32
33#ifdef ARCHITECTURE_x86_64 33#ifdef ARCHITECTURE_x86_64
34#define Crash() __asm__ __volatile__("int $3") 34#define Crash() __asm__ __volatile__("int $3")
35#elif defined(ARCHITECTURE_ARM)
36#define Crash() __asm__ __volatile__("trap")
37#else 35#else
38#define Crash() exit(1) 36#define Crash() exit(1)
39#endif 37#endif
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 6799a357a..df2ce80b1 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -32,6 +32,7 @@
32#define SDMC_DIR "sdmc" 32#define SDMC_DIR "sdmc"
33#define NAND_DIR "nand" 33#define NAND_DIR "nand"
34#define SYSDATA_DIR "sysdata" 34#define SYSDATA_DIR "sysdata"
35#define KEYS_DIR "keys"
35#define LOG_DIR "log" 36#define LOG_DIR "log"
36 37
37// Filenames 38// Filenames
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index b8dd92b65..7aeda737f 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -706,6 +706,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
706 paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP); 706 paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
707 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); 707 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
708 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); 708 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
709 paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
709 // TODO: Put the logs in a better location for each OS 710 // TODO: Put the logs in a better location for each OS
710 paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP); 711 paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
711 } 712 }
@@ -736,6 +737,19 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
736 return paths[path]; 737 return paths[path];
737} 738}
738 739
740std::string GetHactoolConfigurationPath() {
741#ifdef _WIN32
742 PWSTR pw_local_path = nullptr;
743 if (SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &pw_local_path) != S_OK)
744 return "";
745 std::string local_path = Common::UTF16ToUTF8(pw_local_path);
746 CoTaskMemFree(pw_local_path);
747 return local_path + "\\.switch";
748#else
749 return GetHomeDirectory() + "/.switch";
750#endif
751}
752
739size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { 753size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
740 return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); 754 return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
741} 755}
diff --git a/src/common/file_util.h b/src/common/file_util.h
index bc9272d89..28697d527 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -23,6 +23,7 @@ namespace FileUtil {
23enum class UserPath { 23enum class UserPath {
24 CacheDir, 24 CacheDir,
25 ConfigDir, 25 ConfigDir,
26 KeysDir,
26 LogDir, 27 LogDir,
27 NANDDir, 28 NANDDir,
28 RootDir, 29 RootDir,
@@ -125,6 +126,8 @@ bool SetCurrentDir(const std::string& directory);
125// directory. To be used in "multi-user" mode (that is, installed). 126// directory. To be used in "multi-user" mode (that is, installed).
126const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); 127const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
127 128
129std::string GetHactoolConfigurationPath();
130
128// Returns the path to where the sys file are 131// Returns the path to where the sys file are
129std::string GetSysDirectory(); 132std::string GetSysDirectory();
130 133
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index ad9edbcdf..355abd682 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -168,26 +168,41 @@ void FileBackend::Write(const Entry& entry) {
168 SUB(Service, AM) \ 168 SUB(Service, AM) \
169 SUB(Service, AOC) \ 169 SUB(Service, AOC) \
170 SUB(Service, APM) \ 170 SUB(Service, APM) \
171 SUB(Service, ARP) \
171 SUB(Service, BCAT) \ 172 SUB(Service, BCAT) \
173 SUB(Service, BPC) \
174 SUB(Service, BTM) \
175 SUB(Service, Capture) \
172 SUB(Service, Fatal) \ 176 SUB(Service, Fatal) \
177 SUB(Service, FGM) \
173 SUB(Service, Friend) \ 178 SUB(Service, Friend) \
174 SUB(Service, FS) \ 179 SUB(Service, FS) \
175 SUB(Service, HID) \ 180 SUB(Service, HID) \
181 SUB(Service, LBL) \
176 SUB(Service, LDN) \ 182 SUB(Service, LDN) \
177 SUB(Service, LM) \ 183 SUB(Service, LM) \
184 SUB(Service, Migration) \
185 SUB(Service, Mii) \
178 SUB(Service, MM) \ 186 SUB(Service, MM) \
187 SUB(Service, NCM) \
188 SUB(Service, NFC) \
179 SUB(Service, NFP) \ 189 SUB(Service, NFP) \
180 SUB(Service, NIFM) \ 190 SUB(Service, NIFM) \
181 SUB(Service, NS) \ 191 SUB(Service, NS) \
182 SUB(Service, NVDRV) \ 192 SUB(Service, NVDRV) \
193 SUB(Service, PCIE) \
183 SUB(Service, PCTL) \ 194 SUB(Service, PCTL) \
195 SUB(Service, PCV) \
184 SUB(Service, PREPO) \ 196 SUB(Service, PREPO) \
197 SUB(Service, PSC) \
185 SUB(Service, SET) \ 198 SUB(Service, SET) \
186 SUB(Service, SM) \ 199 SUB(Service, SM) \
187 SUB(Service, SPL) \ 200 SUB(Service, SPL) \
188 SUB(Service, SSL) \ 201 SUB(Service, SSL) \
189 SUB(Service, Time) \ 202 SUB(Service, Time) \
203 SUB(Service, USB) \
190 SUB(Service, VI) \ 204 SUB(Service, VI) \
205 SUB(Service, WLAN) \
191 CLS(HW) \ 206 CLS(HW) \
192 SUB(HW, Memory) \ 207 SUB(HW, Memory) \
193 SUB(HW, LCD) \ 208 SUB(HW, LCD) \
@@ -204,6 +219,7 @@ void FileBackend::Write(const Entry& entry) {
204 CLS(Input) \ 219 CLS(Input) \
205 CLS(Network) \ 220 CLS(Network) \
206 CLS(Loader) \ 221 CLS(Loader) \
222 CLS(Crypto) \
207 CLS(WebService) 223 CLS(WebService)
208 224
209// GetClassName is a macro defined by Windows.h, grrr... 225// GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index ad3cbf5d1..a889ebefa 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -54,27 +54,42 @@ enum class Class : ClassType {
54 Service_AM, ///< The AM (Applet manager) service 54 Service_AM, ///< The AM (Applet manager) service
55 Service_AOC, ///< The AOC (AddOn Content) service 55 Service_AOC, ///< The AOC (AddOn Content) service
56 Service_APM, ///< The APM (Performance) service 56 Service_APM, ///< The APM (Performance) service
57 Service_ARP, ///< The ARP service
57 Service_Audio, ///< The Audio (Audio control) service 58 Service_Audio, ///< The Audio (Audio control) service
58 Service_BCAT, ///< The BCAT service 59 Service_BCAT, ///< The BCAT service
60 Service_BPC, ///< The BPC service
61 Service_BTM, ///< The BTM service
62 Service_Capture, ///< The capture service
59 Service_Fatal, ///< The Fatal service 63 Service_Fatal, ///< The Fatal service
64 Service_FGM, ///< The FGM service
60 Service_Friend, ///< The friend service 65 Service_Friend, ///< The friend service
61 Service_FS, ///< The FS (Filesystem) service 66 Service_FS, ///< The FS (Filesystem) service
62 Service_HID, ///< The HID (Human interface device) service 67 Service_HID, ///< The HID (Human interface device) service
68 Service_LBL, ///< The LBL (LCD backlight) service
63 Service_LDN, ///< The LDN (Local domain network) service 69 Service_LDN, ///< The LDN (Local domain network) service
64 Service_LM, ///< The LM (Logger) service 70 Service_LM, ///< The LM (Logger) service
71 Service_Migration, ///< The migration service
72 Service_Mii, ///< The Mii service
65 Service_MM, ///< The MM (Multimedia) service 73 Service_MM, ///< The MM (Multimedia) service
74 Service_NCM, ///< The NCM service
75 Service_NFC, ///< The NFC (Near-field communication) service
66 Service_NFP, ///< The NFP service 76 Service_NFP, ///< The NFP service
67 Service_NIFM, ///< The NIFM (Network interface) service 77 Service_NIFM, ///< The NIFM (Network interface) service
68 Service_NS, ///< The NS services 78 Service_NS, ///< The NS services
69 Service_NVDRV, ///< The NVDRV (Nvidia driver) service 79 Service_NVDRV, ///< The NVDRV (Nvidia driver) service
80 Service_PCIE, ///< The PCIe service
70 Service_PCTL, ///< The PCTL (Parental control) service 81 Service_PCTL, ///< The PCTL (Parental control) service
82 Service_PCV, ///< The PCV service
71 Service_PREPO, ///< The PREPO (Play report) service 83 Service_PREPO, ///< The PREPO (Play report) service
84 Service_PSC, ///< The PSC service
72 Service_SET, ///< The SET (Settings) service 85 Service_SET, ///< The SET (Settings) service
73 Service_SM, ///< The SM (Service manager) service 86 Service_SM, ///< The SM (Service manager) service
74 Service_SPL, ///< The SPL service 87 Service_SPL, ///< The SPL service
75 Service_SSL, ///< The SSL service 88 Service_SSL, ///< The SSL service
76 Service_Time, ///< The time service 89 Service_Time, ///< The time service
90 Service_USB, ///< The USB (Universal Serial Bus) service
77 Service_VI, ///< The VI (Video interface) service 91 Service_VI, ///< The VI (Video interface) service
92 Service_WLAN, ///< The WLAN (Wireless local area network) service
78 HW, ///< Low-level hardware emulation 93 HW, ///< Low-level hardware emulation
79 HW_Memory, ///< Memory-map and address translation 94 HW_Memory, ///< Memory-map and address translation
80 HW_LCD, ///< LCD register emulation 95 HW_LCD, ///< LCD register emulation
@@ -89,6 +104,7 @@ enum class Class : ClassType {
89 Audio_DSP, ///< The HLE implementation of the DSP 104 Audio_DSP, ///< The HLE implementation of the DSP
90 Audio_Sink, ///< Emulator audio output backend 105 Audio_Sink, ///< Emulator audio output backend
91 Loader, ///< ROM loader 106 Loader, ///< ROM loader
107 Crypto, ///< Cryptographic engine/functions
92 Input, ///< Input emulation 108 Input, ///< Input emulation
93 Network, ///< Network emulation 109 Network, ///< Network emulation
94 WebService, ///< Interface to yuzu Web Services 110 WebService, ///< Interface to yuzu Web Services
diff --git a/src/common/logging/text_formatter.h b/src/common/logging/text_formatter.h
index c587faefb..9609cec7c 100644
--- a/src/common/logging/text_formatter.h
+++ b/src/common/logging/text_formatter.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <string>
8 9
9namespace Log { 10namespace Log {
10 11
diff --git a/src/common/math_util.h b/src/common/math_util.h
index c6a83c953..343cdd902 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -19,12 +19,12 @@ inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start
19 19
20template <class T> 20template <class T>
21struct Rectangle { 21struct Rectangle {
22 T left; 22 T left{};
23 T top; 23 T top{};
24 T right; 24 T right{};
25 T bottom; 25 T bottom{};
26 26
27 Rectangle() {} 27 Rectangle() = default;
28 28
29 Rectangle(T left, T top, T right, T bottom) 29 Rectangle(T left, T top, T right, T bottom)
30 : left(left), top(top), right(right), bottom(bottom) {} 30 : left(left), top(top), right(right), bottom(bottom) {}
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 1f0456aee..0ca663032 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -2,12 +2,12 @@
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 <cctype> 6#include <cctype>
6#include <cerrno> 7#include <cerrno>
7#include <cstdio> 8#include <cstdio>
8#include <cstdlib> 9#include <cstdlib>
9#include <cstring> 10#include <cstring>
10#include <boost/range/algorithm/transform.hpp>
11#include "common/common_paths.h" 11#include "common/common_paths.h"
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/string_util.h" 13#include "common/string_util.h"
@@ -24,13 +24,15 @@ namespace Common {
24 24
25/// Make a string lowercase 25/// Make a string lowercase
26std::string ToLower(std::string str) { 26std::string ToLower(std::string str) {
27 boost::transform(str, str.begin(), ::tolower); 27 std::transform(str.begin(), str.end(), str.begin(),
28 [](unsigned char c) { return std::tolower(c); });
28 return str; 29 return str;
29} 30}
30 31
31/// Make a string uppercase 32/// Make a string uppercase
32std::string ToUpper(std::string str) { 33std::string ToUpper(std::string str) {
33 boost::transform(str, str.begin(), ::toupper); 34 std::transform(str.begin(), str.end(), str.begin(),
35 [](unsigned char c) { return std::toupper(c); });
34 return str; 36 return str;
35} 37}
36 38
diff --git a/src/common/swap.h b/src/common/swap.h
index fc7af4280..32af0b6ac 100644
--- a/src/common/swap.h
+++ b/src/common/swap.h
@@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) {
69inline u64 swap64(u64 _data) { 69inline u64 swap64(u64 _data) {
70 return _byteswap_uint64(_data); 70 return _byteswap_uint64(_data);
71} 71}
72#elif ARCHITECTURE_ARM 72#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6)
73inline u16 swap16(u16 _data) { 73inline u16 swap16(u16 _data) {
74 u32 data = _data; 74 u32 data = _data;
75 __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); 75 __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data));
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index a0c731e8c..edf13bc49 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -33,9 +33,11 @@ public:
33 bool Empty() const { 33 bool Empty() const {
34 return !read_ptr->next.load(); 34 return !read_ptr->next.load();
35 } 35 }
36
36 T& Front() const { 37 T& Front() const {
37 return read_ptr->current; 38 return read_ptr->current;
38 } 39 }
40
39 template <typename Arg> 41 template <typename Arg>
40 void Push(Arg&& t) { 42 void Push(Arg&& t) {
41 // create the element, add it to the queue 43 // create the element, add it to the queue
@@ -108,15 +110,41 @@ private:
108// single reader, multiple writer queue 110// single reader, multiple writer queue
109 111
110template <typename T, bool NeedSize = true> 112template <typename T, bool NeedSize = true>
111class MPSCQueue : public SPSCQueue<T, NeedSize> { 113class MPSCQueue {
112public: 114public:
115 u32 Size() const {
116 return spsc_queue.Size();
117 }
118
119 bool Empty() const {
120 return spsc_queue.Empty();
121 }
122
123 T& Front() const {
124 return spsc_queue.Front();
125 }
126
113 template <typename Arg> 127 template <typename Arg>
114 void Push(Arg&& t) { 128 void Push(Arg&& t) {
115 std::lock_guard<std::mutex> lock(write_lock); 129 std::lock_guard<std::mutex> lock(write_lock);
116 SPSCQueue<T, NeedSize>::Push(t); 130 spsc_queue.Push(t);
131 }
132
133 void Pop() {
134 return spsc_queue.Pop();
135 }
136
137 bool Pop(T& t) {
138 return spsc_queue.Pop(t);
139 }
140
141 // not thread-safe
142 void Clear() {
143 spsc_queue.Clear();
117 } 144 }
118 145
119private: 146private:
147 SPSCQueue<T, NeedSize> spsc_queue;
120 std::mutex write_lock; 148 std::mutex write_lock;
121}; 149};
122} // namespace Common 150} // namespace Common
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index f0c5b1a43..2dc15e434 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -3,31 +3,16 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <ctime> 5#include <ctime>
6
7#include <fmt/format.h> 6#include <fmt/format.h>
8
9#ifdef _WIN32
10#include <windows.h>
11// windows.h needs to be included before other windows headers
12#include <mmsystem.h>
13#include <sys/timeb.h>
14#else
15#include <sys/time.h>
16#endif
17#include "common/common_types.h" 7#include "common/common_types.h"
18#include "common/string_util.h" 8#include "common/string_util.h"
19#include "common/timer.h" 9#include "common/timer.h"
20 10
21namespace Common { 11namespace Common {
22 12
23u32 Timer::GetTimeMs() { 13std::chrono::milliseconds Timer::GetTimeMs() {
24#ifdef _WIN32 14 return std::chrono::duration_cast<std::chrono::milliseconds>(
25 return timeGetTime(); 15 std::chrono::system_clock::now().time_since_epoch());
26#else
27 struct timeval t;
28 (void)gettimeofday(&t, nullptr);
29 return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000));
30#endif
31} 16}
32 17
33// -------------------------------------------- 18// --------------------------------------------
@@ -63,7 +48,7 @@ void Timer::Update() {
63// ------------------------------------- 48// -------------------------------------
64 49
65// Get the number of milliseconds since the last Update() 50// Get the number of milliseconds since the last Update()
66u64 Timer::GetTimeDifference() { 51std::chrono::milliseconds Timer::GetTimeDifference() {
67 return GetTimeMs() - m_LastTime; 52 return GetTimeMs() - m_LastTime;
68} 53}
69 54
@@ -74,11 +59,11 @@ void Timer::AddTimeDifference() {
74} 59}
75 60
76// Get the time elapsed since the Start() 61// Get the time elapsed since the Start()
77u64 Timer::GetTimeElapsed() { 62std::chrono::milliseconds Timer::GetTimeElapsed() {
78 // If we have not started yet, return 1 (because then I don't 63 // If we have not started yet, return 1 (because then I don't
79 // have to change the FPS calculation in CoreRerecording.cpp . 64 // have to change the FPS calculation in CoreRerecording.cpp .
80 if (m_StartTime == 0) 65 if (m_StartTime.count() == 0)
81 return 1; 66 return std::chrono::milliseconds(1);
82 67
83 // Return the final timer time if the timer is stopped 68 // Return the final timer time if the timer is stopped
84 if (!m_Running) 69 if (!m_Running)
@@ -90,49 +75,34 @@ u64 Timer::GetTimeElapsed() {
90// Get the formatted time elapsed since the Start() 75// Get the formatted time elapsed since the Start()
91std::string Timer::GetTimeElapsedFormatted() const { 76std::string Timer::GetTimeElapsedFormatted() const {
92 // If we have not started yet, return zero 77 // If we have not started yet, return zero
93 if (m_StartTime == 0) 78 if (m_StartTime.count() == 0)
94 return "00:00:00:000"; 79 return "00:00:00:000";
95 80
96 // The number of milliseconds since the start. 81 // The number of milliseconds since the start.
97 // Use a different value if the timer is stopped. 82 // Use a different value if the timer is stopped.
98 u64 Milliseconds; 83 std::chrono::milliseconds Milliseconds;
99 if (m_Running) 84 if (m_Running)
100 Milliseconds = GetTimeMs() - m_StartTime; 85 Milliseconds = GetTimeMs() - m_StartTime;
101 else 86 else
102 Milliseconds = m_LastTime - m_StartTime; 87 Milliseconds = m_LastTime - m_StartTime;
103 // Seconds 88 // Seconds
104 u32 Seconds = (u32)(Milliseconds / 1000); 89 std::chrono::seconds Seconds = std::chrono::duration_cast<std::chrono::seconds>(Milliseconds);
105 // Minutes 90 // Minutes
106 u32 Minutes = Seconds / 60; 91 std::chrono::minutes Minutes = std::chrono::duration_cast<std::chrono::minutes>(Milliseconds);
107 // Hours 92 // Hours
108 u32 Hours = Minutes / 60; 93 std::chrono::hours Hours = std::chrono::duration_cast<std::chrono::hours>(Milliseconds);
109 94
110 std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours, Minutes % 60, Seconds % 60, 95 std::string TmpStr = fmt::format("{:02}:{:02}:{:02}:{:03}", Hours.count(), Minutes.count() % 60,
111 Milliseconds % 1000); 96 Seconds.count() % 60, Milliseconds.count() % 1000);
112 return TmpStr; 97 return TmpStr;
113} 98}
114 99
115// Get current time
116void Timer::IncreaseResolution() {
117#ifdef _WIN32
118 timeBeginPeriod(1);
119#endif
120}
121
122void Timer::RestoreResolution() {
123#ifdef _WIN32
124 timeEndPeriod(1);
125#endif
126}
127
128// Get the number of seconds since January 1 1970 100// Get the number of seconds since January 1 1970
129u64 Timer::GetTimeSinceJan1970() { 101std::chrono::seconds Timer::GetTimeSinceJan1970() {
130 time_t ltime; 102 return std::chrono::duration_cast<std::chrono::seconds>(GetTimeMs());
131 time(&ltime);
132 return ((u64)ltime);
133} 103}
134 104
135u64 Timer::GetLocalTimeSinceJan1970() { 105std::chrono::seconds Timer::GetLocalTimeSinceJan1970() {
136 time_t sysTime, tzDiff, tzDST; 106 time_t sysTime, tzDiff, tzDST;
137 struct tm* gmTime; 107 struct tm* gmTime;
138 108
@@ -149,7 +119,7 @@ u64 Timer::GetLocalTimeSinceJan1970() {
149 gmTime = gmtime(&sysTime); 119 gmTime = gmtime(&sysTime);
150 tzDiff = sysTime - mktime(gmTime); 120 tzDiff = sysTime - mktime(gmTime);
151 121
152 return (u64)(sysTime + tzDiff + tzDST); 122 return std::chrono::seconds(sysTime + tzDiff + tzDST);
153} 123}
154 124
155// Return the current time formatted as Minutes:Seconds:Milliseconds 125// Return the current time formatted as Minutes:Seconds:Milliseconds
@@ -164,30 +134,16 @@ std::string Timer::GetTimeFormatted() {
164 134
165 strftime(tmp, 6, "%M:%S", gmTime); 135 strftime(tmp, 6, "%M:%S", gmTime);
166 136
167// Now tack on the milliseconds 137 u64 milliseconds = static_cast<u64>(GetTimeMs().count()) % 1000;
168#ifdef _WIN32 138 return fmt::format("{}:{:03}", tmp, milliseconds);
169 struct timeb tp;
170 (void)::ftime(&tp);
171 return fmt::format("{}:{:03}", tmp, tp.millitm);
172#else
173 struct timeval t;
174 (void)gettimeofday(&t, nullptr);
175 return fmt::format("{}:{:03}", tmp, static_cast<int>(t.tv_usec / 1000));
176#endif
177} 139}
178 140
179// Returns a timestamp with decimals for precise time comparisons 141// Returns a timestamp with decimals for precise time comparisons
180// ---------------- 142// ----------------
181double Timer::GetDoubleTime() { 143double Timer::GetDoubleTime() {
182#ifdef _WIN32
183 struct timeb tp;
184 (void)::ftime(&tp);
185#else
186 struct timeval t;
187 (void)gettimeofday(&t, nullptr);
188#endif
189 // Get continuous timestamp 144 // Get continuous timestamp
190 u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); 145 u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count());
146 double ms = static_cast<u64>(GetTimeMs().count()) % 1000;
191 147
192 // Remove a few years. We only really want enough seconds to make 148 // Remove a few years. We only really want enough seconds to make
193 // sure that we are detecting actual actions, perhaps 60 seconds is 149 // sure that we are detecting actual actions, perhaps 60 seconds is
@@ -196,12 +152,7 @@ double Timer::GetDoubleTime() {
196 TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60); 152 TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
197 153
198 // Make a smaller integer that fits in the double 154 // Make a smaller integer that fits in the double
199 u32 Seconds = (u32)TmpSeconds; 155 u32 Seconds = static_cast<u32>(TmpSeconds);
200#ifdef _WIN32
201 double ms = tp.millitm / 1000.0 / 1000.0;
202#else
203 double ms = t.tv_usec / 1000000.0;
204#endif
205 double TmpTime = Seconds + ms; 156 double TmpTime = Seconds + ms;
206 157
207 return TmpTime; 158 return TmpTime;
diff --git a/src/common/timer.h b/src/common/timer.h
index 78d37426b..27b521baa 100644
--- a/src/common/timer.h
+++ b/src/common/timer.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <chrono>
7#include <string> 8#include <string>
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
@@ -18,24 +19,22 @@ public:
18 19
19 // The time difference is always returned in milliseconds, regardless of alternative internal 20 // The time difference is always returned in milliseconds, regardless of alternative internal
20 // representation 21 // representation
21 u64 GetTimeDifference(); 22 std::chrono::milliseconds GetTimeDifference();
22 void AddTimeDifference(); 23 void AddTimeDifference();
23 24
24 static void IncreaseResolution(); 25 static std::chrono::seconds GetTimeSinceJan1970();
25 static void RestoreResolution(); 26 static std::chrono::seconds GetLocalTimeSinceJan1970();
26 static u64 GetTimeSinceJan1970();
27 static u64 GetLocalTimeSinceJan1970();
28 static double GetDoubleTime(); 27 static double GetDoubleTime();
29 28
30 static std::string GetTimeFormatted(); 29 static std::string GetTimeFormatted();
31 std::string GetTimeElapsedFormatted() const; 30 std::string GetTimeElapsedFormatted() const;
32 u64 GetTimeElapsed(); 31 std::chrono::milliseconds GetTimeElapsed();
33 32
34 static u32 GetTimeMs(); 33 static std::chrono::milliseconds GetTimeMs();
35 34
36private: 35private:
37 u64 m_LastTime; 36 std::chrono::milliseconds m_LastTime;
38 u64 m_StartTime; 37 std::chrono::milliseconds m_StartTime;
39 bool m_Running; 38 bool m_Running;
40}; 39};
41 40
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b74e495ef..0abf7edc1 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -12,6 +12,16 @@ add_library(core STATIC
12 core_timing.h 12 core_timing.h
13 core_timing_util.cpp 13 core_timing_util.cpp
14 core_timing_util.h 14 core_timing_util.h
15 crypto/aes_util.cpp
16 crypto/aes_util.h
17 crypto/encryption_layer.cpp
18 crypto/encryption_layer.h
19 crypto/key_manager.cpp
20 crypto/key_manager.h
21 crypto/ctr_encryption_layer.cpp
22 crypto/ctr_encryption_layer.h
23 file_sys/card_image.cpp
24 file_sys/card_image.h
15 file_sys/content_archive.cpp 25 file_sys/content_archive.cpp
16 file_sys/content_archive.h 26 file_sys/content_archive.h
17 file_sys/control_metadata.cpp 27 file_sys/control_metadata.cpp
@@ -23,6 +33,8 @@ add_library(core STATIC
23 file_sys/partition_filesystem.h 33 file_sys/partition_filesystem.h
24 file_sys/program_metadata.cpp 34 file_sys/program_metadata.cpp
25 file_sys/program_metadata.h 35 file_sys/program_metadata.h
36 file_sys/romfs.cpp
37 file_sys/romfs.h
26 file_sys/romfs_factory.cpp 38 file_sys/romfs_factory.cpp
27 file_sys/romfs_factory.h 39 file_sys/romfs_factory.h
28 file_sys/savedata_factory.cpp 40 file_sys/savedata_factory.cpp
@@ -35,6 +47,8 @@ add_library(core STATIC
35 file_sys/vfs_offset.h 47 file_sys/vfs_offset.h
36 file_sys/vfs_real.cpp 48 file_sys/vfs_real.cpp
37 file_sys/vfs_real.h 49 file_sys/vfs_real.h
50 file_sys/vfs_vector.cpp
51 file_sys/vfs_vector.h
38 frontend/emu_window.cpp 52 frontend/emu_window.cpp
39 frontend/emu_window.h 53 frontend/emu_window.h
40 frontend/framebuffer_layout.cpp 54 frontend/framebuffer_layout.cpp
@@ -59,12 +73,10 @@ add_library(core STATIC
59 hle/kernel/hle_ipc.h 73 hle/kernel/hle_ipc.h
60 hle/kernel/kernel.cpp 74 hle/kernel/kernel.cpp
61 hle/kernel/kernel.h 75 hle/kernel/kernel.h
62 hle/kernel/memory.cpp
63 hle/kernel/memory.h
64 hle/kernel/mutex.cpp 76 hle/kernel/mutex.cpp
65 hle/kernel/mutex.h 77 hle/kernel/mutex.h
66 hle/kernel/object_address_table.cpp 78 hle/kernel/object.cpp
67 hle/kernel/object_address_table.h 79 hle/kernel/object.h
68 hle/kernel/process.cpp 80 hle/kernel/process.cpp
69 hle/kernel/process.h 81 hle/kernel/process.h
70 hle/kernel/resource_limit.cpp 82 hle/kernel/resource_limit.cpp
@@ -110,23 +122,41 @@ add_library(core STATIC
110 hle/service/am/applet_ae.h 122 hle/service/am/applet_ae.h
111 hle/service/am/applet_oe.cpp 123 hle/service/am/applet_oe.cpp
112 hle/service/am/applet_oe.h 124 hle/service/am/applet_oe.h
125 hle/service/am/idle.cpp
126 hle/service/am/idle.h
127 hle/service/am/omm.cpp
128 hle/service/am/omm.h
129 hle/service/am/spsm.cpp
130 hle/service/am/spsm.h
113 hle/service/aoc/aoc_u.cpp 131 hle/service/aoc/aoc_u.cpp
114 hle/service/aoc/aoc_u.h 132 hle/service/aoc/aoc_u.h
115 hle/service/apm/apm.cpp 133 hle/service/apm/apm.cpp
116 hle/service/apm/apm.h 134 hle/service/apm/apm.h
117 hle/service/apm/interface.cpp 135 hle/service/apm/interface.cpp
118 hle/service/apm/interface.h 136 hle/service/apm/interface.h
137 hle/service/arp/arp.cpp
138 hle/service/arp/arp.h
139 hle/service/audio/audctl.cpp
140 hle/service/audio/audctl.h
141 hle/service/audio/auddbg.cpp
142 hle/service/audio/auddbg.h
143 hle/service/audio/audin_a.cpp
144 hle/service/audio/audin_a.h
119 hle/service/audio/audin_u.cpp 145 hle/service/audio/audin_u.cpp
120 hle/service/audio/audin_u.h 146 hle/service/audio/audin_u.h
121 hle/service/audio/audio.cpp 147 hle/service/audio/audio.cpp
122 hle/service/audio/audio.h 148 hle/service/audio/audio.h
149 hle/service/audio/audout_a.cpp
150 hle/service/audio/audout_a.h
123 hle/service/audio/audout_u.cpp 151 hle/service/audio/audout_u.cpp
124 hle/service/audio/audout_u.h 152 hle/service/audio/audout_u.h
153 hle/service/audio/audrec_a.cpp
154 hle/service/audio/audrec_a.h
125 hle/service/audio/audrec_u.cpp 155 hle/service/audio/audrec_u.cpp
126 hle/service/audio/audrec_u.h 156 hle/service/audio/audrec_u.h
157 hle/service/audio/audren_a.cpp
158 hle/service/audio/audren_a.h
127 hle/service/audio/audren_u.cpp 159 hle/service/audio/audren_u.cpp
128 hle/service/audio/audren_u.cpp
129 hle/service/audio/audren_u.h
130 hle/service/audio/audren_u.h 160 hle/service/audio/audren_u.h
131 hle/service/audio/codecctl.cpp 161 hle/service/audio/codecctl.cpp
132 hle/service/audio/codecctl.h 162 hle/service/audio/codecctl.h
@@ -136,6 +166,14 @@ add_library(core STATIC
136 hle/service/bcat/bcat.h 166 hle/service/bcat/bcat.h
137 hle/service/bcat/module.cpp 167 hle/service/bcat/module.cpp
138 hle/service/bcat/module.h 168 hle/service/bcat/module.h
169 hle/service/bpc/bpc.cpp
170 hle/service/bpc/bpc.h
171 hle/service/btdrv/btdrv.cpp
172 hle/service/btdrv/btdrv.h
173 hle/service/btm/btm.cpp
174 hle/service/btm/btm.h
175 hle/service/caps/caps.cpp
176 hle/service/caps/caps.h
139 hle/service/erpt/erpt.cpp 177 hle/service/erpt/erpt.cpp
140 hle/service/erpt/erpt.h 178 hle/service/erpt/erpt.h
141 hle/service/es/es.cpp 179 hle/service/es/es.cpp
@@ -150,8 +188,14 @@ add_library(core STATIC
150 hle/service/fatal/fatal_u.h 188 hle/service/fatal/fatal_u.h
151 hle/service/filesystem/filesystem.cpp 189 hle/service/filesystem/filesystem.cpp
152 hle/service/filesystem/filesystem.h 190 hle/service/filesystem/filesystem.h
191 hle/service/filesystem/fsp_ldr.cpp
192 hle/service/filesystem/fsp_ldr.h
193 hle/service/filesystem/fsp_pr.cpp
194 hle/service/filesystem/fsp_pr.h
153 hle/service/filesystem/fsp_srv.cpp 195 hle/service/filesystem/fsp_srv.cpp
154 hle/service/filesystem/fsp_srv.h 196 hle/service/filesystem/fsp_srv.h
197 hle/service/fgm/fgm.cpp
198 hle/service/fgm/fgm.h
155 hle/service/friend/friend.cpp 199 hle/service/friend/friend.cpp
156 hle/service/friend/friend.h 200 hle/service/friend/friend.h
157 hle/service/friend/interface.cpp 201 hle/service/friend/interface.cpp
@@ -164,14 +208,24 @@ add_library(core STATIC
164 hle/service/hid/irs.h 208 hle/service/hid/irs.h
165 hle/service/hid/xcd.cpp 209 hle/service/hid/xcd.cpp
166 hle/service/hid/xcd.h 210 hle/service/hid/xcd.h
211 hle/service/lbl/lbl.cpp
212 hle/service/lbl/lbl.h
167 hle/service/ldn/ldn.cpp 213 hle/service/ldn/ldn.cpp
168 hle/service/ldn/ldn.h 214 hle/service/ldn/ldn.h
169 hle/service/ldr/ldr.cpp 215 hle/service/ldr/ldr.cpp
170 hle/service/ldr/ldr.h 216 hle/service/ldr/ldr.h
171 hle/service/lm/lm.cpp 217 hle/service/lm/lm.cpp
172 hle/service/lm/lm.h 218 hle/service/lm/lm.h
219 hle/service/mig/mig.cpp
220 hle/service/mig/mig.h
221 hle/service/mii/mii.cpp
222 hle/service/mii/mii.h
173 hle/service/mm/mm_u.cpp 223 hle/service/mm/mm_u.cpp
174 hle/service/mm/mm_u.h 224 hle/service/mm/mm_u.h
225 hle/service/ncm/ncm.cpp
226 hle/service/ncm/ncm.h
227 hle/service/nfc/nfc.cpp
228 hle/service/nfc/nfc.h
175 hle/service/nfp/nfp.cpp 229 hle/service/nfp/nfp.cpp
176 hle/service/nfp/nfp.h 230 hle/service/nfp/nfp.h
177 hle/service/nfp/nfp_user.cpp 231 hle/service/nfp/nfp_user.cpp
@@ -209,14 +263,20 @@ add_library(core STATIC
209 hle/service/nvflinger/buffer_queue.h 263 hle/service/nvflinger/buffer_queue.h
210 hle/service/nvflinger/nvflinger.cpp 264 hle/service/nvflinger/nvflinger.cpp
211 hle/service/nvflinger/nvflinger.h 265 hle/service/nvflinger/nvflinger.h
266 hle/service/pcie/pcie.cpp
267 hle/service/pcie/pcie.h
212 hle/service/pctl/module.cpp 268 hle/service/pctl/module.cpp
213 hle/service/pctl/module.h 269 hle/service/pctl/module.h
214 hle/service/pctl/pctl.cpp 270 hle/service/pctl/pctl.cpp
215 hle/service/pctl/pctl.h 271 hle/service/pctl/pctl.h
272 hle/service/pcv/pcv.cpp
273 hle/service/pcv/pcv.h
216 hle/service/pm/pm.cpp 274 hle/service/pm/pm.cpp
217 hle/service/pm/pm.h 275 hle/service/pm/pm.h
218 hle/service/prepo/prepo.cpp 276 hle/service/prepo/prepo.cpp
219 hle/service/prepo/prepo.h 277 hle/service/prepo/prepo.h
278 hle/service/psc/psc.cpp
279 hle/service/psc/psc.h
220 hle/service/service.cpp 280 hle/service/service.cpp
221 hle/service/service.h 281 hle/service/service.h
222 hle/service/set/set.cpp 282 hle/service/set/set.cpp
@@ -255,6 +315,8 @@ add_library(core STATIC
255 hle/service/time/interface.h 315 hle/service/time/interface.h
256 hle/service/time/time.cpp 316 hle/service/time/time.cpp
257 hle/service/time/time.h 317 hle/service/time/time.h
318 hle/service/usb/usb.cpp
319 hle/service/usb/usb.h
258 hle/service/vi/vi.cpp 320 hle/service/vi/vi.cpp
259 hle/service/vi/vi.h 321 hle/service/vi/vi.h
260 hle/service/vi/vi_m.cpp 322 hle/service/vi/vi_m.cpp
@@ -263,10 +325,8 @@ add_library(core STATIC
263 hle/service/vi/vi_s.h 325 hle/service/vi/vi_s.h
264 hle/service/vi/vi_u.cpp 326 hle/service/vi/vi_u.cpp
265 hle/service/vi/vi_u.h 327 hle/service/vi/vi_u.h
266 hw/hw.cpp 328 hle/service/wlan/wlan.cpp
267 hw/hw.h 329 hle/service/wlan/wlan.h
268 hw/lcd.cpp
269 hw/lcd.h
270 loader/deconstructed_rom_directory.cpp 330 loader/deconstructed_rom_directory.cpp
271 loader/deconstructed_rom_directory.h 331 loader/deconstructed_rom_directory.h
272 loader/elf.cpp 332 loader/elf.cpp
@@ -281,6 +341,8 @@ add_library(core STATIC
281 loader/nro.h 341 loader/nro.h
282 loader/nso.cpp 342 loader/nso.cpp
283 loader/nso.h 343 loader/nso.h
344 loader/xci.cpp
345 loader/xci.h
284 memory.cpp 346 memory.cpp
285 memory.h 347 memory.h
286 memory_hook.cpp 348 memory_hook.cpp
@@ -299,8 +361,8 @@ add_library(core STATIC
299 361
300create_target_directory_groups(core) 362create_target_directory_groups(core)
301 363
302target_link_libraries(core PUBLIC common PRIVATE video_core) 364target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
303target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) 365target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn)
304 366
305if (ARCHITECTURE_x86_64) 367if (ARCHITECTURE_x86_64)
306 target_sources(core PRIVATE 368 target_sources(core PRIVATE
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 57b8634b9..ceb3f7683 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -10,7 +10,7 @@
10#include "core/arm/dynarmic/arm_dynarmic.h" 10#include "core/arm/dynarmic/arm_dynarmic.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/memory.h" 13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/svc.h" 14#include "core/hle/kernel/svc.h"
15#include "core/memory.h" 15#include "core/memory.h"
16 16
@@ -139,14 +139,12 @@ void ARM_Dynarmic::Step() {
139} 139}
140 140
141ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index) 141ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index)
142 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), 142 : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
143 jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>( 143 exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
144 exclusive_monitor)}, 144 ThreadContext ctx;
145 core_index{core_index} {
146 ARM_Interface::ThreadContext ctx;
147 inner_unicorn.SaveContext(ctx); 145 inner_unicorn.SaveContext(ctx);
148 LoadContext(ctx);
149 PageTableChanged(); 146 PageTableChanged();
147 LoadContext(ctx);
150} 148}
151 149
152ARM_Dynarmic::~ARM_Dynarmic() = default; 150ARM_Dynarmic::~ARM_Dynarmic() = default;
@@ -205,7 +203,7 @@ u64 ARM_Dynarmic::GetTlsAddress() const {
205 return cb->tpidrro_el0; 203 return cb->tpidrro_el0;
206} 204}
207 205
208void ARM_Dynarmic::SetTlsAddress(u64 address) { 206void ARM_Dynarmic::SetTlsAddress(VAddr address) {
209 cb->tpidrro_el0 = address; 207 cb->tpidrro_el0 = address;
210} 208}
211 209
@@ -217,7 +215,7 @@ void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
217 cb->tpidr_el0 = value; 215 cb->tpidr_el0 = value;
218} 216}
219 217
220void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { 218void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
221 ctx.cpu_registers = jit->GetRegisters(); 219 ctx.cpu_registers = jit->GetRegisters();
222 ctx.sp = jit->GetSP(); 220 ctx.sp = jit->GetSP();
223 ctx.pc = jit->GetPC(); 221 ctx.pc = jit->GetPC();
@@ -226,7 +224,7 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
226 ctx.fpscr = jit->GetFpcr(); 224 ctx.fpscr = jit->GetFpcr();
227} 225}
228 226
229void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { 227void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
230 jit->SetRegisters(ctx.cpu_registers); 228 jit->SetRegisters(ctx.cpu_registers);
231 jit->SetSP(ctx.sp); 229 jit->SetSP(ctx.sp);
232 jit->SetPC(ctx.pc); 230 jit->SetPC(ctx.pc);
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index 4c11f35a4..6bc349460 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
203 } 203 }
204 Kernel::Thread* thread = Kernel::GetCurrentThread(); 204 Kernel::Thread* thread = Kernel::GetCurrentThread();
205 SaveContext(thread->context); 205 SaveContext(thread->context);
206 if (last_bkpt_hit || (num_instructions == 1)) { 206 if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
207 last_bkpt_hit = false; 207 last_bkpt_hit = false;
208 GDBStub::Break(); 208 GDBStub::Break();
209 GDBStub::SendTrap(thread, 5); 209 GDBStub::SendTrap(thread, 5);
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b7f4b4532..085ba68d0 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -15,11 +15,10 @@
15#include "core/hle/service/service.h" 15#include "core/hle/service/service.h"
16#include "core/hle/service/sm/controller.h" 16#include "core/hle/service/sm/controller.h"
17#include "core/hle/service/sm/sm.h" 17#include "core/hle/service/sm/sm.h"
18#include "core/hw/hw.h"
19#include "core/loader/loader.h" 18#include "core/loader/loader.h"
20#include "core/memory_setup.h"
21#include "core/settings.h" 19#include "core/settings.h"
22#include "file_sys/vfs_real.h" 20#include "file_sys/vfs_real.h"
21#include "video_core/renderer_base.h"
23#include "video_core/video_core.h" 22#include "video_core/video_core.h"
24 23
25namespace Core { 24namespace Core {
@@ -63,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
63 // execute. Otherwise, get out of the loop function. 62 // execute. Otherwise, get out of the loop function.
64 if (GDBStub::GetCpuHaltFlag()) { 63 if (GDBStub::GetCpuHaltFlag()) {
65 if (GDBStub::GetCpuStepFlag()) { 64 if (GDBStub::GetCpuStepFlag()) {
66 GDBStub::SetCpuStepFlag(false);
67 tight_loop = false; 65 tight_loop = false;
68 } else { 66 } else {
69 return ResultStatus::Success; 67 return ResultStatus::Success;
@@ -79,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
79 } 77 }
80 } 78 }
81 79
80 if (GDBStub::IsServerEnabled()) {
81 GDBStub::SetCpuStepFlag(false);
82 }
83
82 return status; 84 return status;
83} 85}
84 86
@@ -86,7 +88,7 @@ System::ResultStatus System::SingleStep() {
86 return RunLoop(false); 88 return RunLoop(false);
87} 89}
88 90
89System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { 91System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
90 app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); 92 app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath));
91 93
92 if (!app_loader) { 94 if (!app_loader) {
@@ -101,8 +103,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
101 static_cast<int>(system_mode.second)); 103 static_cast<int>(system_mode.second));
102 104
103 switch (system_mode.second) { 105 switch (system_mode.second) {
104 case Loader::ResultStatus::ErrorEncrypted: 106 case Loader::ResultStatus::ErrorMissingKeys:
105 return ResultStatus::ErrorLoader_ErrorEncrypted; 107 return ResultStatus::ErrorLoader_ErrorMissingKeys;
108 case Loader::ResultStatus::ErrorDecrypting:
109 return ResultStatus::ErrorLoader_ErrorDecrypting;
106 case Loader::ResultStatus::ErrorInvalidFormat: 110 case Loader::ResultStatus::ErrorInvalidFormat:
107 return ResultStatus::ErrorLoader_ErrorInvalidFormat; 111 return ResultStatus::ErrorLoader_ErrorInvalidFormat;
108 case Loader::ResultStatus::ErrorUnsupportedArch: 112 case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -112,7 +116,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
112 } 116 }
113 } 117 }
114 118
115 ResultStatus init_result{Init(emu_window, system_mode.first.get())}; 119 ResultStatus init_result{Init(emu_window)};
116 if (init_result != ResultStatus::Success) { 120 if (init_result != ResultStatus::Success) {
117 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", 121 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
118 static_cast<int>(init_result)); 122 static_cast<int>(init_result));
@@ -126,8 +130,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
126 System::Shutdown(); 130 System::Shutdown();
127 131
128 switch (load_result) { 132 switch (load_result) {
129 case Loader::ResultStatus::ErrorEncrypted: 133 case Loader::ResultStatus::ErrorMissingKeys:
130 return ResultStatus::ErrorLoader_ErrorEncrypted; 134 return ResultStatus::ErrorLoader_ErrorMissingKeys;
135 case Loader::ResultStatus::ErrorDecrypting:
136 return ResultStatus::ErrorLoader_ErrorDecrypting;
131 case Loader::ResultStatus::ErrorInvalidFormat: 137 case Loader::ResultStatus::ErrorInvalidFormat:
132 return ResultStatus::ErrorLoader_ErrorInvalidFormat; 138 return ResultStatus::ErrorLoader_ErrorInvalidFormat;
133 case Loader::ResultStatus::ErrorUnsupportedArch: 139 case Loader::ResultStatus::ErrorUnsupportedArch:
@@ -163,7 +169,7 @@ Cpu& System::CpuCore(size_t core_index) {
163 return *cpu_cores[core_index]; 169 return *cpu_cores[core_index];
164} 170}
165 171
166System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 172System::ResultStatus System::Init(EmuWindow& emu_window) {
167 LOG_DEBUG(HW_Memory, "initialized OK"); 173 LOG_DEBUG(HW_Memory, "initialized OK");
168 174
169 CoreTiming::Init(); 175 CoreTiming::Init();
@@ -176,19 +182,20 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
176 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); 182 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
177 } 183 }
178 184
179 gpu_core = std::make_unique<Tegra::GPU>();
180 telemetry_session = std::make_unique<Core::TelemetrySession>(); 185 telemetry_session = std::make_unique<Core::TelemetrySession>();
181 service_manager = std::make_shared<Service::SM::ServiceManager>(); 186 service_manager = std::make_shared<Service::SM::ServiceManager>();
182 187
183 HW::Init(); 188 Kernel::Init();
184 Kernel::Init(system_mode);
185 Service::Init(service_manager); 189 Service::Init(service_manager);
186 GDBStub::Init(); 190 GDBStub::Init();
187 191
188 if (!VideoCore::Init(emu_window)) { 192 renderer = VideoCore::CreateRenderer(emu_window);
193 if (!renderer->Init()) {
189 return ResultStatus::ErrorVideoCore; 194 return ResultStatus::ErrorVideoCore;
190 } 195 }
191 196
197 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
198
192 // Create threads for CPU cores 1-3, and build thread_to_cpu map 199 // Create threads for CPU cores 1-3, and build thread_to_cpu map
193 // CPU core 0 is run on the main thread 200 // CPU core 0 is run on the main thread
194 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 201 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
@@ -220,11 +227,10 @@ void System::Shutdown() {
220 perf_results.frametime * 1000.0); 227 perf_results.frametime * 1000.0);
221 228
222 // Shutdown emulation session 229 // Shutdown emulation session
223 VideoCore::Shutdown(); 230 renderer.reset();
224 GDBStub::Shutdown(); 231 GDBStub::Shutdown();
225 Service::Shutdown(); 232 Service::Shutdown();
226 Kernel::Shutdown(); 233 Kernel::Shutdown();
227 HW::Shutdown();
228 service_manager.reset(); 234 service_manager.reset();
229 telemetry_session.reset(); 235 telemetry_session.reset();
230 gpu_core.reset(); 236 gpu_core.reset();
diff --git a/src/core/core.h b/src/core/core.h
index c123fe401..c8ca4b247 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -11,7 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/core_cpu.h" 13#include "core/core_cpu.h"
14#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/object.h"
15#include "core/hle/kernel/scheduler.h" 15#include "core/hle/kernel/scheduler.h"
16#include "core/loader/loader.h" 16#include "core/loader/loader.h"
17#include "core/memory.h" 17#include "core/memory.h"
@@ -27,6 +27,10 @@ namespace Service::SM {
27class ServiceManager; 27class ServiceManager;
28} 28}
29 29
30namespace VideoCore {
31class RendererBase;
32}
33
30namespace Core { 34namespace Core {
31 35
32class System { 36class System {
@@ -43,12 +47,14 @@ public:
43 47
44 /// Enumeration representing the return values of the System Initialize and Load process. 48 /// Enumeration representing the return values of the System Initialize and Load process.
45 enum class ResultStatus : u32 { 49 enum class ResultStatus : u32 {
46 Success, ///< Succeeded 50 Success, ///< Succeeded
47 ErrorNotInitialized, ///< Error trying to use core prior to initialization 51 ErrorNotInitialized, ///< Error trying to use core prior to initialization
48 ErrorGetLoader, ///< Error finding the correct application loader 52 ErrorGetLoader, ///< Error finding the correct application loader
49 ErrorSystemMode, ///< Error determining the system mode 53 ErrorSystemMode, ///< Error determining the system mode
50 ErrorLoader, ///< Error loading the specified application 54 ErrorLoader, ///< Error loading the specified application
51 ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption 55 ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be
56 ///< found.
57 ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption
52 ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an 58 ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
53 /// invalid format 59 /// invalid format
54 ErrorSystemFiles, ///< Error in finding system files 60 ErrorSystemFiles, ///< Error in finding system files
@@ -76,16 +82,28 @@ public:
76 */ 82 */
77 ResultStatus SingleStep(); 83 ResultStatus SingleStep();
78 84
85 /**
86 * Invalidate the CPU instruction caches
87 * This function should only be used by GDB Stub to support breakpoints, memory updates and
88 * step/continue commands.
89 */
90 void InvalidateCpuInstructionCaches() {
91 for (auto& cpu : cpu_cores) {
92 cpu->ArmInterface().ClearInstructionCache();
93 }
94 }
95
79 /// Shutdown the emulated system. 96 /// Shutdown the emulated system.
80 void Shutdown(); 97 void Shutdown();
81 98
82 /** 99 /**
83 * Load an executable application. 100 * Load an executable application.
84 * @param emu_window Pointer to the host-system window used for video output and keyboard input. 101 * @param emu_window Reference to the host-system window used for video output and keyboard
102 * input.
85 * @param filepath String path to the executable application to load on the host file system. 103 * @param filepath String path to the executable application to load on the host file system.
86 * @returns ResultStatus code, indicating if the operation succeeded. 104 * @returns ResultStatus code, indicating if the operation succeeded.
87 */ 105 */
88 ResultStatus Load(EmuWindow* emu_window, const std::string& filepath); 106 ResultStatus Load(EmuWindow& emu_window, const std::string& filepath);
89 107
90 /** 108 /**
91 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 109 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@@ -126,11 +144,26 @@ public:
126 /// Gets a CPU interface to the CPU core with the specified index 144 /// Gets a CPU interface to the CPU core with the specified index
127 Cpu& CpuCore(size_t core_index); 145 Cpu& CpuCore(size_t core_index);
128 146
129 /// Gets the GPU interface 147 /// Gets a mutable reference to the GPU interface
130 Tegra::GPU& GPU() { 148 Tegra::GPU& GPU() {
131 return *gpu_core; 149 return *gpu_core;
132 } 150 }
133 151
152 /// Gets an immutable reference to the GPU interface.
153 const Tegra::GPU& GPU() const {
154 return *gpu_core;
155 }
156
157 /// Gets a mutable reference to the renderer.
158 VideoCore::RendererBase& Renderer() {
159 return *renderer;
160 }
161
162 /// Gets an immutable reference to the renderer.
163 const VideoCore::RendererBase& Renderer() const {
164 return *renderer;
165 }
166
134 /// Gets the scheduler for the CPU core that is currently running 167 /// Gets the scheduler for the CPU core that is currently running
135 Kernel::Scheduler& CurrentScheduler() { 168 Kernel::Scheduler& CurrentScheduler() {
136 return *CurrentCpuCore().Scheduler(); 169 return *CurrentCpuCore().Scheduler();
@@ -186,14 +219,15 @@ private:
186 219
187 /** 220 /**
188 * Initialize the emulated system. 221 * Initialize the emulated system.
189 * @param emu_window Pointer to the host-system window used for video output and keyboard input. 222 * @param emu_window Reference to the host-system window used for video output and keyboard
190 * @param system_mode The system mode. 223 * input.
191 * @return ResultStatus code, indicating if the operation succeeded. 224 * @return ResultStatus code, indicating if the operation succeeded.
192 */ 225 */
193 ResultStatus Init(EmuWindow* emu_window, u32 system_mode); 226 ResultStatus Init(EmuWindow& emu_window);
194 227
195 /// AppLoader used to load the current executing application 228 /// AppLoader used to load the current executing application
196 std::unique_ptr<Loader::AppLoader> app_loader; 229 std::unique_ptr<Loader::AppLoader> app_loader;
230 std::unique_ptr<VideoCore::RendererBase> renderer;
197 std::unique_ptr<Tegra::GPU> gpu_core; 231 std::unique_ptr<Tegra::GPU> gpu_core;
198 std::shared_ptr<Tegra::DebugContext> debug_context; 232 std::shared_ptr<Tegra::DebugContext> debug_context;
199 Kernel::SharedPtr<Kernel::Process> current_process; 233 Kernel::SharedPtr<Kernel::Process> current_process;
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 54e15a701..46a522fcd 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -12,7 +12,6 @@
12#include "core/arm/unicorn/arm_unicorn.h" 12#include "core/arm/unicorn/arm_unicorn.h"
13#include "core/core_cpu.h" 13#include "core/core_cpu.h"
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/scheduler.h" 15#include "core/hle/kernel/scheduler.h"
17#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
18#include "core/settings.h" 17#include "core/settings.h"
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index a1b6f96f1..d3bb6f818 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user
141 ForceExceptionCheck(cycles_into_future); 141 ForceExceptionCheck(cycles_into_future);
142 142
143 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 143 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
144 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 144 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
145} 145}
146 146
147void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 147void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
@@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
156 // Removing random items breaks the invariant so we have to re-establish it. 156 // Removing random items breaks the invariant so we have to re-establish it.
157 if (itr != event_queue.end()) { 157 if (itr != event_queue.end()) {
158 event_queue.erase(itr, event_queue.end()); 158 event_queue.erase(itr, event_queue.end());
159 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 159 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
160 } 160 }
161} 161}
162 162
@@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) {
167 // Removing random items breaks the invariant so we have to re-establish it. 167 // Removing random items breaks the invariant so we have to re-establish it.
168 if (itr != event_queue.end()) { 168 if (itr != event_queue.end()) {
169 event_queue.erase(itr, event_queue.end()); 169 event_queue.erase(itr, event_queue.end());
170 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 170 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
171 } 171 }
172} 172}
173 173
@@ -190,7 +190,7 @@ void MoveEvents() {
190 for (Event ev; ts_queue.Pop(ev);) { 190 for (Event ev; ts_queue.Pop(ev);) {
191 ev.fifo_order = event_fifo_id++; 191 ev.fifo_order = event_fifo_id++;
192 event_queue.emplace_back(std::move(ev)); 192 event_queue.emplace_back(std::move(ev));
193 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 193 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
194 } 194 }
195} 195}
196 196
@@ -205,7 +205,7 @@ void Advance() {
205 205
206 while (!event_queue.empty() && event_queue.front().time <= global_timer) { 206 while (!event_queue.empty() && event_queue.front().time <= global_timer) {
207 Event evt = std::move(event_queue.front()); 207 Event evt = std::move(event_queue.front());
208 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); 208 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
209 event_queue.pop_back(); 209 event_queue.pop_back();
210 evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time)); 210 evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
211 } 211 }
@@ -226,8 +226,8 @@ void Idle() {
226 downcount = 0; 226 downcount = 0;
227} 227}
228 228
229u64 GetGlobalTimeUs() { 229std::chrono::microseconds GetGlobalTimeUs() {
230 return GetTicks() * 1000000 / BASE_CLOCK_RATE; 230 return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
231} 231}
232 232
233int GetDowncount() { 233int GetDowncount() {
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 7fe6380ad..dfa161c0d 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -17,12 +17,17 @@
17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") 17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
18 */ 18 */
19 19
20#include <chrono>
20#include <functional> 21#include <functional>
21#include <string> 22#include <string>
22#include "common/common_types.h" 23#include "common/common_types.h"
23 24
24namespace CoreTiming { 25namespace CoreTiming {
25 26
27struct EventType;
28
29using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
30
26/** 31/**
27 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 32 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
28 * required to end slice -1 and start slice 0 before the first cycle of code is executed. 33 * required to end slice -1 and start slice 0 before the first cycle of code is executed.
@@ -30,8 +35,6 @@ namespace CoreTiming {
30void Init(); 35void Init();
31void Shutdown(); 36void Shutdown();
32 37
33typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback;
34
35/** 38/**
36 * This should only be called from the emu thread, if you are calling it any other thread, you are 39 * This should only be called from the emu thread, if you are calling it any other thread, you are
37 * doing something evil 40 * doing something evil
@@ -40,8 +43,6 @@ u64 GetTicks();
40u64 GetIdleTicks(); 43u64 GetIdleTicks();
41void AddTicks(u64 ticks); 44void AddTicks(u64 ticks);
42 45
43struct EventType;
44
45/** 46/**
46 * Returns the event_type identifier. if name is not unique, it will assert. 47 * Returns the event_type identifier. if name is not unique, it will assert.
47 */ 48 */
@@ -86,7 +87,7 @@ void ClearPendingEvents();
86 87
87void ForceExceptionCheck(s64 cycles); 88void ForceExceptionCheck(s64 cycles);
88 89
89u64 GetGlobalTimeUs(); 90std::chrono::microseconds GetGlobalTimeUs();
90 91
91int GetDowncount(); 92int GetDowncount();
92 93
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
new file mode 100644
index 000000000..a9876c83e
--- /dev/null
+++ b/src/core/crypto/aes_util.cpp
@@ -0,0 +1,115 @@
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 <mbedtls/cipher.h>
6#include "common/assert.h"
7#include "common/logging/log.h"
8#include "core/crypto/aes_util.h"
9#include "core/crypto/key_manager.h"
10
11namespace Core::Crypto {
12namespace {
13std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
14 std::vector<u8> out(0x10);
15 for (size_t i = 0xF; i <= 0xF; --i) {
16 out[i] = sector_id & 0xFF;
17 sector_id >>= 8;
18 }
19 return out;
20}
21} // Anonymous namespace
22
23static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR),
24 "CTR has incorrect value.");
25static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB),
26 "ECB has incorrect value.");
27static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS),
28 "XTS has incorrect value.");
29
30// Structure to hide mbedtls types from header file
31struct CipherContext {
32 mbedtls_cipher_context_t encryption_context;
33 mbedtls_cipher_context_t decryption_context;
34};
35
36template <typename Key, size_t KeySize>
37Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode)
38 : ctx(std::make_unique<CipherContext>()) {
39 mbedtls_cipher_init(&ctx->encryption_context);
40 mbedtls_cipher_init(&ctx->decryption_context);
41
42 ASSERT_MSG((mbedtls_cipher_setup(
43 &ctx->encryption_context,
44 mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) ||
45 mbedtls_cipher_setup(
46 &ctx->decryption_context,
47 mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0,
48 "Failed to initialize mbedtls ciphers.");
49
50 ASSERT(
51 !mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT));
52 ASSERT(
53 !mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT));
54 //"Failed to set key on mbedtls ciphers.");
55}
56
57template <typename Key, size_t KeySize>
58AESCipher<Key, KeySize>::~AESCipher() {
59 mbedtls_cipher_free(&ctx->encryption_context);
60 mbedtls_cipher_free(&ctx->decryption_context);
61}
62
63template <typename Key, size_t KeySize>
64void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) {
65 ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) ||
66 mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0,
67 "Failed to set IV on mbedtls ciphers.");
68}
69
70template <typename Key, size_t KeySize>
71void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const {
72 auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context;
73
74 mbedtls_cipher_reset(context);
75
76 size_t written = 0;
77 if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
78 mbedtls_cipher_update(context, src, size, dest, &written);
79 if (written != size) {
80 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
81 size, written);
82 }
83 } else {
84 const auto block_size = mbedtls_cipher_get_block_size(context);
85
86 for (size_t offset = 0; offset < size; offset += block_size) {
87 auto length = std::min<size_t>(block_size, size - offset);
88 mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
89 if (written != length) {
90 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
91 length, written);
92 }
93 }
94 }
95
96 mbedtls_cipher_finish(context, nullptr, nullptr);
97}
98
99template <typename Key, size_t KeySize>
100void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id,
101 size_t sector_size, Op op) {
102 if (size % sector_size > 0) {
103 LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size.");
104 return;
105 }
106
107 for (size_t i = 0; i < size; i += sector_size) {
108 SetIV(CalculateNintendoTweak(sector_id++));
109 Transcode<u8, u8>(src + i, sector_size, dest + i, op);
110 }
111}
112
113template class AESCipher<Key128>;
114template class AESCipher<Key256>;
115} // namespace Core::Crypto \ No newline at end of file
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
new file mode 100644
index 000000000..8ce9d6612
--- /dev/null
+++ b/src/core/crypto/aes_util.h
@@ -0,0 +1,64 @@
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#include <type_traits>
9#include <vector>
10#include "common/common_types.h"
11#include "core/file_sys/vfs.h"
12
13namespace Core::Crypto {
14
15struct CipherContext;
16
17enum class Mode {
18 CTR = 11,
19 ECB = 2,
20 XTS = 70,
21};
22
23enum class Op {
24 Encrypt,
25 Decrypt,
26};
27
28template <typename Key, size_t KeySize = sizeof(Key)>
29class AESCipher {
30 static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8.");
31 static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
32
33public:
34 AESCipher(Key key, Mode mode);
35
36 ~AESCipher();
37
38 void SetIV(std::vector<u8> iv);
39
40 template <typename Source, typename Dest>
41 void Transcode(const Source* src, size_t size, Dest* dest, Op op) const {
42 static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
43 "Transcode source and destination types must be trivially copyable.");
44 Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op);
45 }
46
47 void Transcode(const u8* src, size_t size, u8* dest, Op op) const;
48
49 template <typename Source, typename Dest>
50 void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
51 size_t sector_size, Op op) {
52 static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>,
53 "XTSTranscode source and destination types must be trivially copyable.");
54 XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id,
55 sector_size, op);
56 }
57
58 void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size,
59 Op op);
60
61private:
62 std::unique_ptr<CipherContext> ctx;
63};
64} // namespace Core::Crypto
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp
new file mode 100644
index 000000000..106db02b3
--- /dev/null
+++ b/src/core/crypto/ctr_encryption_layer.cpp
@@ -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#include <cstring>
6#include "common/assert.h"
7#include "core/crypto/ctr_encryption_layer.h"
8
9namespace Core::Crypto {
10
11CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset)
12 : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
13 iv(16, 0) {}
14
15size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
16 if (length == 0)
17 return 0;
18
19 const auto sector_offset = offset & 0xF;
20 if (sector_offset == 0) {
21 UpdateIV(base_offset + offset);
22 std::vector<u8> raw = base->ReadBytes(length, offset);
23 if (raw.size() != length)
24 return Read(data, raw.size(), offset);
25 cipher.Transcode(raw.data(), length, data, Op::Decrypt);
26 return length;
27 }
28
29 // offset does not fall on block boundary (0x10)
30 std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset);
31 UpdateIV(base_offset + offset - sector_offset);
32 cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt);
33 size_t read = 0x10 - sector_offset;
34
35 if (length + sector_offset < 0x10) {
36 std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read));
37 return read;
38 }
39 std::memcpy(data, block.data() + sector_offset, read);
40 return read + Read(data + read, length - read, offset + read);
41}
42
43void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) {
44 const auto length = std::min(iv_.size(), iv.size());
45 iv.assign(iv_.cbegin(), iv_.cbegin() + length);
46}
47
48void CTREncryptionLayer::UpdateIV(size_t offset) const {
49 offset >>= 4;
50 for (size_t i = 0; i < 8; ++i) {
51 iv[16 - i - 1] = offset & 0xFF;
52 offset >>= 8;
53 }
54 cipher.SetIV(iv);
55}
56} // namespace Core::Crypto
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h
new file mode 100644
index 000000000..11b8683c7
--- /dev/null
+++ b/src/core/crypto/ctr_encryption_layer.h
@@ -0,0 +1,33 @@
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 "core/crypto/aes_util.h"
9#include "core/crypto/encryption_layer.h"
10#include "core/crypto/key_manager.h"
11
12namespace Core::Crypto {
13
14// Sits on top of a VirtualFile and provides CTR-mode AES decription.
15class CTREncryptionLayer : public EncryptionLayer {
16public:
17 CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset);
18
19 size_t Read(u8* data, size_t length, size_t offset) const override;
20
21 void SetIV(const std::vector<u8>& iv);
22
23private:
24 size_t base_offset;
25
26 // Must be mutable as operations modify cipher contexts.
27 mutable AESCipher<Key128> cipher;
28 mutable std::vector<u8> iv;
29
30 void UpdateIV(size_t offset) const;
31};
32
33} // namespace Core::Crypto
diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp
new file mode 100644
index 000000000..4204527e3
--- /dev/null
+++ b/src/core/crypto/encryption_layer.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 "core/crypto/encryption_layer.h"
6
7namespace Core::Crypto {
8
9EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {}
10
11std::string EncryptionLayer::GetName() const {
12 return base->GetName();
13}
14
15size_t EncryptionLayer::GetSize() const {
16 return base->GetSize();
17}
18
19bool EncryptionLayer::Resize(size_t new_size) {
20 return false;
21}
22
23std::shared_ptr<FileSys::VfsDirectory> EncryptionLayer::GetContainingDirectory() const {
24 return base->GetContainingDirectory();
25}
26
27bool EncryptionLayer::IsWritable() const {
28 return false;
29}
30
31bool EncryptionLayer::IsReadable() const {
32 return true;
33}
34
35size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) {
36 return 0;
37}
38
39bool EncryptionLayer::Rename(std::string_view name) {
40 return base->Rename(name);
41}
42} // namespace Core::Crypto
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h
new file mode 100644
index 000000000..7f05af9b4
--- /dev/null
+++ b/src/core/crypto/encryption_layer.h
@@ -0,0 +1,33 @@
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/file_sys/vfs.h"
9
10namespace Core::Crypto {
11
12// Basically non-functional class that implements all of the methods that are irrelevant to an
13// EncryptionLayer. Reduces duplicate code.
14class EncryptionLayer : public FileSys::VfsFile {
15public:
16 explicit EncryptionLayer(FileSys::VirtualFile base);
17
18 size_t Read(u8* data, size_t length, size_t offset) const override = 0;
19
20 std::string GetName() const override;
21 size_t GetSize() const override;
22 bool Resize(size_t new_size) override;
23 std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
24 bool IsWritable() const override;
25 bool IsReadable() const override;
26 size_t Write(const u8* data, size_t length, size_t offset) override;
27 bool Rename(std::string_view name) override;
28
29protected:
30 FileSys::VirtualFile base;
31};
32
33} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
new file mode 100644
index 000000000..fc45e7ab5
--- /dev/null
+++ b/src/core/crypto/key_manager.cpp
@@ -0,0 +1,208 @@
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 <fstream>
8#include <locale>
9#include <sstream>
10#include <string_view>
11#include "common/common_paths.h"
12#include "common/file_util.h"
13#include "core/crypto/key_manager.h"
14#include "core/settings.h"
15
16namespace Core::Crypto {
17
18static u8 ToHexNibble(char c1) {
19 if (c1 >= 65 && c1 <= 70)
20 return c1 - 55;
21 if (c1 >= 97 && c1 <= 102)
22 return c1 - 87;
23 if (c1 >= 48 && c1 <= 57)
24 return c1 - 48;
25 throw std::logic_error("Invalid hex digit");
26}
27
28template <size_t Size>
29static std::array<u8, Size> HexStringToArray(std::string_view str) {
30 std::array<u8, Size> out{};
31 for (size_t i = 0; i < 2 * Size; i += 2) {
32 auto d1 = str[i];
33 auto d2 = str[i + 1];
34 out[i / 2] = (ToHexNibble(d1) << 4) | ToHexNibble(d2);
35 }
36 return out;
37}
38
39std::array<u8, 16> operator""_array16(const char* str, size_t len) {
40 if (len != 32)
41 throw std::logic_error("Not of correct size.");
42 return HexStringToArray<16>(str);
43}
44
45std::array<u8, 32> operator""_array32(const char* str, size_t len) {
46 if (len != 64)
47 throw std::logic_error("Not of correct size.");
48 return HexStringToArray<32>(str);
49}
50
51KeyManager::KeyManager() {
52 // Initialize keys
53 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
54 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
55 if (Settings::values.use_dev_keys) {
56 dev_mode = true;
57 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false);
58 } else {
59 dev_mode = false;
60 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false);
61 }
62
63 AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
64}
65
66void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
67 std::ifstream file(filename);
68 if (!file.is_open())
69 return;
70
71 std::string line;
72 while (std::getline(file, line)) {
73 std::vector<std::string> out;
74 std::stringstream stream(line);
75 std::string item;
76 while (std::getline(stream, item, '='))
77 out.push_back(std::move(item));
78
79 if (out.size() != 2)
80 continue;
81
82 out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
83 out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
84
85 if (is_title_keys) {
86 auto rights_id_raw = HexStringToArray<16>(out[0]);
87 u128 rights_id{};
88 std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size());
89 Key128 key = HexStringToArray<16>(out[1]);
90 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
91 } else {
92 std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
93 if (s128_file_id.find(out[0]) != s128_file_id.end()) {
94 const auto index = s128_file_id.at(out[0]);
95 Key128 key = HexStringToArray<16>(out[1]);
96 SetKey(index.type, key, index.field1, index.field2);
97 } else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
98 const auto index = s256_file_id.at(out[0]);
99 Key256 key = HexStringToArray<32>(out[1]);
100 SetKey(index.type, key, index.field1, index.field2);
101 }
102 }
103 }
104}
105
106void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
107 const std::string& filename, bool title) {
108 if (FileUtil::Exists(dir1 + DIR_SEP + filename))
109 LoadFromFile(dir1 + DIR_SEP + filename, title);
110 else if (FileUtil::Exists(dir2 + DIR_SEP + filename))
111 LoadFromFile(dir2 + DIR_SEP + filename, title);
112}
113
114bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
115 return s128_keys.find({id, field1, field2}) != s128_keys.end();
116}
117
118bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const {
119 return s256_keys.find({id, field1, field2}) != s256_keys.end();
120}
121
122Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const {
123 if (!HasKey(id, field1, field2))
124 return {};
125 return s128_keys.at({id, field1, field2});
126}
127
128Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
129 if (!HasKey(id, field1, field2))
130 return {};
131 return s256_keys.at({id, field1, field2});
132}
133
134void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
135 s128_keys[{id, field1, field2}] = key;
136}
137
138void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
139 s256_keys[{id, field1, field2}] = key;
140}
141
142bool KeyManager::KeyFileExists(bool title) {
143 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
144 const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
145 if (title) {
146 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") ||
147 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys");
148 }
149
150 if (Settings::values.use_dev_keys) {
151 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") ||
152 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys");
153 }
154
155 return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") ||
156 FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys");
157}
158
159const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
160 {"master_key_00", {S128KeyType::Master, 0, 0}},
161 {"master_key_01", {S128KeyType::Master, 1, 0}},
162 {"master_key_02", {S128KeyType::Master, 2, 0}},
163 {"master_key_03", {S128KeyType::Master, 3, 0}},
164 {"master_key_04", {S128KeyType::Master, 4, 0}},
165 {"package1_key_00", {S128KeyType::Package1, 0, 0}},
166 {"package1_key_01", {S128KeyType::Package1, 1, 0}},
167 {"package1_key_02", {S128KeyType::Package1, 2, 0}},
168 {"package1_key_03", {S128KeyType::Package1, 3, 0}},
169 {"package1_key_04", {S128KeyType::Package1, 4, 0}},
170 {"package2_key_00", {S128KeyType::Package2, 0, 0}},
171 {"package2_key_01", {S128KeyType::Package2, 1, 0}},
172 {"package2_key_02", {S128KeyType::Package2, 2, 0}},
173 {"package2_key_03", {S128KeyType::Package2, 3, 0}},
174 {"package2_key_04", {S128KeyType::Package2, 4, 0}},
175 {"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
176 {"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
177 {"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
178 {"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
179 {"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
180 {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
181 {"key_area_key_application_00",
182 {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}},
183 {"key_area_key_application_01",
184 {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}},
185 {"key_area_key_application_02",
186 {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}},
187 {"key_area_key_application_03",
188 {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}},
189 {"key_area_key_application_04",
190 {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
191 {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
192 {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
193 {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
194 {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
195 {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
196 {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
197 {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
198 {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
199 {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
200 {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
201};
202
203const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
204 {"header_key", {S256KeyType::Header, 0, 0}},
205 {"sd_card_save_key", {S256KeyType::SDSave, 0, 0}},
206 {"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}},
207};
208} // namespace Core::Crypto
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
new file mode 100644
index 000000000..c4c53cefc
--- /dev/null
+++ b/src/core/crypto/key_manager.h
@@ -0,0 +1,120 @@
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 <string>
9#include <type_traits>
10#include <unordered_map>
11#include <vector>
12#include <fmt/format.h>
13#include "common/common_types.h"
14
15namespace Core::Crypto {
16
17using Key128 = std::array<u8, 0x10>;
18using Key256 = std::array<u8, 0x20>;
19using SHA256Hash = std::array<u8, 0x20>;
20
21static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
22static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
23
24enum class S256KeyType : u64 {
25 Header, //
26 SDSave, //
27 SDNCA, //
28};
29
30enum class S128KeyType : u64 {
31 Master, // f1=crypto revision
32 Package1, // f1=crypto revision
33 Package2, // f1=crypto revision
34 Titlekek, // f1=crypto revision
35 ETicketRSAKek, //
36 KeyArea, // f1=crypto revision f2=type {app, ocean, system}
37 SDSeed, //
38 Titlekey, // f1=rights id LSB f2=rights id MSB
39};
40
41enum class KeyAreaKeyType : u8 {
42 Application,
43 Ocean,
44 System,
45};
46
47template <typename KeyType>
48struct KeyIndex {
49 KeyType type;
50 u64 field1;
51 u64 field2;
52
53 std::string DebugInfo() const {
54 u8 key_size = 16;
55 if constexpr (std::is_same_v<KeyType, S256KeyType>)
56 key_size = 32;
57 return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size,
58 static_cast<u8>(type), field1, field2);
59 }
60};
61
62// The following two (== and hash) are so KeyIndex can be a key in unordered_map
63
64template <typename KeyType>
65bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
66 return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2);
67}
68
69template <typename KeyType>
70bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
71 return !operator==(lhs, rhs);
72}
73
74} // namespace Core::Crypto
75
76namespace std {
77template <typename KeyType>
78struct hash<Core::Crypto::KeyIndex<KeyType>> {
79 size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const {
80 using std::hash;
81
82 return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^
83 (hash<u64>()(k.field2) << 1);
84 }
85};
86} // namespace std
87
88namespace Core::Crypto {
89
90std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
91std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);
92
93class KeyManager {
94public:
95 KeyManager();
96
97 bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
98 bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
99
100 Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
101 Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
102
103 void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
104 void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
105
106 static bool KeyFileExists(bool title);
107
108private:
109 std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys;
110 std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
111
112 bool dev_mode;
113 void LoadFromFile(const std::string& filename, bool is_title_keys);
114 void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
115 const std::string& filename, bool title);
116
117 static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
118 static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
119};
120} // namespace Core::Crypto
diff --git a/src/core/crypto/sha_util.cpp b/src/core/crypto/sha_util.cpp
new file mode 100644
index 000000000..180008a85
--- /dev/null
+++ b/src/core/crypto/sha_util.cpp
@@ -0,0 +1,5 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5namespace Crypto {} // namespace Crypto
diff --git a/src/core/crypto/sha_util.h b/src/core/crypto/sha_util.h
new file mode 100644
index 000000000..fa3fa9d33
--- /dev/null
+++ b/src/core/crypto/sha_util.h
@@ -0,0 +1,20 @@
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/assert.h"
8#include "core/file_sys/vfs.h"
9#include "key_manager.h"
10#include "mbedtls/cipher.h"
11
12namespace Crypto {
13typedef std::array<u8, 0x20> SHA256Hash;
14
15inline SHA256Hash operator"" _HASH(const char* data, size_t len) {
16 if (len != 0x40)
17 return {};
18}
19
20} // namespace Crypto
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
new file mode 100644
index 000000000..395eea8ae
--- /dev/null
+++ b/src/core/file_sys/card_image.cpp
@@ -0,0 +1,149 @@
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 <array>
6#include <string>
7#include <core/loader/loader.h>
8#include "core/file_sys/card_image.h"
9#include "core/file_sys/partition_filesystem.h"
10#include "core/file_sys/vfs_offset.h"
11
12namespace FileSys {
13
14XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
15 if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
16 status = Loader::ResultStatus::ErrorInvalidFormat;
17 return;
18 }
19
20 if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
21 status = Loader::ResultStatus::ErrorInvalidFormat;
22 return;
23 }
24
25 PartitionFilesystem main_hfs(
26 std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset));
27
28 if (main_hfs.GetStatus() != Loader::ResultStatus::Success) {
29 status = main_hfs.GetStatus();
30 return;
31 }
32
33 static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure",
34 "logo"};
35
36 for (XCIPartition partition :
37 {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
38 auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
39 if (raw != nullptr)
40 partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
41 }
42
43 auto result = AddNCAFromPartition(XCIPartition::Secure);
44 if (result != Loader::ResultStatus::Success) {
45 status = result;
46 return;
47 }
48
49 result = AddNCAFromPartition(XCIPartition::Update);
50 if (result != Loader::ResultStatus::Success) {
51 status = result;
52 return;
53 }
54
55 result = AddNCAFromPartition(XCIPartition::Normal);
56 if (result != Loader::ResultStatus::Success) {
57 status = result;
58 return;
59 }
60
61 if (GetFormatVersion() >= 0x2) {
62 result = AddNCAFromPartition(XCIPartition::Logo);
63 if (result != Loader::ResultStatus::Success) {
64 status = result;
65 return;
66 }
67 }
68
69 status = Loader::ResultStatus::Success;
70}
71
72Loader::ResultStatus XCI::GetStatus() const {
73 return status;
74}
75
76VirtualDir XCI::GetPartition(XCIPartition partition) const {
77 return partitions[static_cast<size_t>(partition)];
78}
79
80VirtualDir XCI::GetSecurePartition() const {
81 return GetPartition(XCIPartition::Secure);
82}
83
84VirtualDir XCI::GetNormalPartition() const {
85 return GetPartition(XCIPartition::Normal);
86}
87
88VirtualDir XCI::GetUpdatePartition() const {
89 return GetPartition(XCIPartition::Update);
90}
91
92VirtualDir XCI::GetLogoPartition() const {
93 return GetPartition(XCIPartition::Logo);
94}
95
96std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
97 const auto iter =
98 std::find_if(ncas.begin(), ncas.end(),
99 [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; });
100 return iter == ncas.end() ? nullptr : *iter;
101}
102
103VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
104 auto nca = GetNCAByType(type);
105 if (nca != nullptr)
106 return nca->GetBaseFile();
107 return nullptr;
108}
109
110std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const {
111 return {};
112}
113
114std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const {
115 return std::vector<std::shared_ptr<VfsDirectory>>();
116}
117
118std::string XCI::GetName() const {
119 return file->GetName();
120}
121
122std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const {
123 return file->GetContainingDirectory();
124}
125
126bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
127 return false;
128}
129
130Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
131 if (partitions[static_cast<size_t>(part)] == nullptr) {
132 return Loader::ResultStatus::ErrorInvalidFormat;
133 }
134
135 for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
136 if (file->GetExtension() != "nca")
137 continue;
138 auto nca = std::make_shared<NCA>(file);
139 if (nca->GetStatus() == Loader::ResultStatus::Success)
140 ncas.push_back(std::move(nca));
141 }
142
143 return Loader::ResultStatus::Success;
144}
145
146u8 XCI::GetFormatVersion() const {
147 return GetLogoPartition() == nullptr ? 0x1 : 0x2;
148}
149} // namespace FileSys
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
new file mode 100644
index 000000000..e089d737c
--- /dev/null
+++ b/src/core/file_sys/card_image.h
@@ -0,0 +1,96 @@
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 <vector>
9#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/vfs.h"
13#include "core/loader/loader.h"
14
15namespace FileSys {
16
17enum class GamecardSize : u8 {
18 S_1GB = 0xFA,
19 S_2GB = 0xF8,
20 S_4GB = 0xF0,
21 S_8GB = 0xE0,
22 S_16GB = 0xE1,
23 S_32GB = 0xE2,
24};
25
26struct GamecardInfo {
27 std::array<u8, 0x70> data;
28};
29static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size.");
30
31struct GamecardHeader {
32 std::array<u8, 0x100> signature;
33 u32_le magic;
34 u32_le secure_area_start;
35 u32_le backup_area_start;
36 u8 kek_index;
37 GamecardSize size;
38 u8 header_version;
39 u8 flags;
40 u64_le package_id;
41 u64_le valid_data_end;
42 u128 info_iv;
43 u64_le hfs_offset;
44 u64_le hfs_size;
45 std::array<u8, 0x20> hfs_header_hash;
46 std::array<u8, 0x20> initial_data_hash;
47 u32_le secure_mode_flag;
48 u32_le title_key_flag;
49 u32_le key_flag;
50 u32_le normal_area_end;
51 GamecardInfo info;
52};
53static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size.");
54
55enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
56
57class XCI : public ReadOnlyVfsDirectory {
58public:
59 explicit XCI(VirtualFile file);
60
61 Loader::ResultStatus GetStatus() const;
62
63 u8 GetFormatVersion() const;
64
65 VirtualDir GetPartition(XCIPartition partition) const;
66 VirtualDir GetSecurePartition() const;
67 VirtualDir GetNormalPartition() const;
68 VirtualDir GetUpdatePartition() const;
69 VirtualDir GetLogoPartition() const;
70
71 std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
72 VirtualFile GetNCAFileByType(NCAContentType type) const;
73
74 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
75
76 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
77
78 std::string GetName() const override;
79
80 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
81
82protected:
83 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
84
85private:
86 Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
87
88 VirtualFile file;
89 GamecardHeader header{};
90
91 Loader::ResultStatus status;
92
93 std::vector<VirtualDir> partitions;
94 std::vector<std::shared_ptr<NCA>> ncas;
95};
96} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index d6b20c047..3529166ac 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -4,9 +4,12 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <utility> 6#include <utility>
7 7#include <boost/optional.hpp>
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/crypto/aes_util.h"
10#include "core/crypto/ctr_encryption_layer.h"
9#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/romfs.h"
10#include "core/file_sys/vfs_offset.h" 13#include "core/file_sys/vfs_offset.h"
11#include "core/loader/loader.h" 14#include "core/loader/loader.h"
12 15
@@ -28,11 +31,19 @@ enum class NCASectionFilesystemType : u8 {
28struct NCASectionHeaderBlock { 31struct NCASectionHeaderBlock {
29 INSERT_PADDING_BYTES(3); 32 INSERT_PADDING_BYTES(3);
30 NCASectionFilesystemType filesystem_type; 33 NCASectionFilesystemType filesystem_type;
31 u8 crypto_type; 34 NCASectionCryptoType crypto_type;
32 INSERT_PADDING_BYTES(3); 35 INSERT_PADDING_BYTES(3);
33}; 36};
34static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); 37static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
35 38
39struct NCASectionRaw {
40 NCASectionHeaderBlock header;
41 std::array<u8, 0x138> block_data;
42 std::array<u8, 0x8> section_ctr;
43 INSERT_PADDING_BYTES(0xB8);
44};
45static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
46
36struct PFS0Superblock { 47struct PFS0Superblock {
37 NCASectionHeaderBlock header_block; 48 NCASectionHeaderBlock header_block;
38 std::array<u8, 0x20> hash; 49 std::array<u8, 0x20> hash;
@@ -42,79 +53,200 @@ struct PFS0Superblock {
42 u64_le hash_table_size; 53 u64_le hash_table_size;
43 u64_le pfs0_header_offset; 54 u64_le pfs0_header_offset;
44 u64_le pfs0_size; 55 u64_le pfs0_size;
45 INSERT_PADDING_BYTES(432); 56 INSERT_PADDING_BYTES(0x1B0);
46}; 57};
47static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); 58static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
48 59
49struct IVFCLevel {
50 u64_le offset;
51 u64_le size;
52 u32_le block_size;
53 u32_le reserved;
54};
55static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
56
57struct RomFSSuperblock { 60struct RomFSSuperblock {
58 NCASectionHeaderBlock header_block; 61 NCASectionHeaderBlock header_block;
59 u32_le magic; 62 IVFCHeader ivfc;
60 u32_le magic_number; 63 INSERT_PADDING_BYTES(0x118);
61 INSERT_PADDING_BYTES(8);
62 std::array<IVFCLevel, 6> levels;
63 INSERT_PADDING_BYTES(64);
64}; 64};
65static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); 65static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
66
67union NCASectionHeader {
68 NCASectionRaw raw;
69 PFS0Superblock pfs0;
70 RomFSSuperblock romfs;
71};
72static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
73
74bool IsValidNCA(const NCAHeader& header) {
75 // TODO(DarkLordZach): Add NCA2/NCA0 support.
76 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
77}
78
79u8 NCA::GetCryptoRevision() const {
80 u8 master_key_id = header.crypto_type;
81 if (header.crypto_type_2 > master_key_id)
82 master_key_id = header.crypto_type_2;
83 if (master_key_id > 0)
84 --master_key_id;
85 return master_key_id;
86}
87
88boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
89 const auto master_key_id = GetCryptoRevision();
90
91 if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
92 return boost::none;
93
94 std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
95 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
96 keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index),
97 Core::Crypto::Mode::ECB);
98 cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
99
100 Core::Crypto::Key128 out;
101 if (type == NCASectionCryptoType::XTS)
102 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
103 else if (type == NCASectionCryptoType::CTR)
104 std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
105 else
106 LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
107 static_cast<u8>(type));
108 u128 out_128{};
109 memcpy(out_128.data(), out.data(), 16);
110 LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
111 master_key_id, header.key_index, out_128[1], out_128[0]);
112
113 return out;
114}
115
116boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const {
117 const auto master_key_id = GetCryptoRevision();
118
119 u128 rights_id{};
120 memcpy(rights_id.data(), header.rights_id.data(), 16);
121 if (rights_id == u128{})
122 return boost::none;
123
124 auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
125 if (titlekey == Core::Crypto::Key128{})
126 return boost::none;
127 Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
128 keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
129 cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
130
131 return titlekey;
132}
133
134VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const {
135 if (!encrypted)
136 return in;
137
138 switch (s_header.raw.header.crypto_type) {
139 case NCASectionCryptoType::NONE:
140 LOG_DEBUG(Crypto, "called with mode=NONE");
141 return in;
142 case NCASectionCryptoType::CTR:
143 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
144 {
145 boost::optional<Core::Crypto::Key128> key = boost::none;
146 if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
147 [](char c) { return c == 0; }) == header.rights_id.end()) {
148 key = GetKeyAreaKey(NCASectionCryptoType::CTR);
149 } else {
150 key = GetTitlekey();
151 }
152
153 if (key == boost::none)
154 return nullptr;
155 auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
156 std::move(in), key.value(), starting_offset);
157 std::vector<u8> iv(16);
158 for (u8 i = 0; i < 8; ++i)
159 iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
160 out->SetIV(iv);
161 return std::static_pointer_cast<VfsFile>(out);
162 }
163 case NCASectionCryptoType::XTS:
164 // TODO(DarkLordZach): Implement XTSEncryptionLayer.
165 default:
166 LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
167 static_cast<u8>(s_header.raw.header.crypto_type));
168 return nullptr;
169 }
170}
66 171
67NCA::NCA(VirtualFile file_) : file(std::move(file_)) { 172NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
68 if (sizeof(NCAHeader) != file->ReadObject(&header)) 173 if (sizeof(NCAHeader) != file->ReadObject(&header))
69 LOG_CRITICAL(Loader, "File reader errored out during header read."); 174 LOG_ERROR(Loader, "File reader errored out during header read.");
175
176 encrypted = false;
70 177
71 if (!IsValidNCA(header)) { 178 if (!IsValidNCA(header)) {
72 status = Loader::ResultStatus::ErrorInvalidFormat; 179 NCAHeader dec_header{};
73 return; 180 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
181 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
182 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
183 Core::Crypto::Op::Decrypt);
184 if (IsValidNCA(dec_header)) {
185 header = dec_header;
186 encrypted = true;
187 } else {
188 if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
189 status = Loader::ResultStatus::ErrorMissingKeys;
190 else
191 status = Loader::ResultStatus::ErrorDecrypting;
192 return;
193 }
74 } 194 }
75 195
76 std::ptrdiff_t number_sections = 196 const std::ptrdiff_t number_sections =
77 std::count_if(std::begin(header.section_tables), std::end(header.section_tables), 197 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
78 [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); 198 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
79 199
200 std::vector<NCASectionHeader> sections(number_sections);
201 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
202
203 if (encrypted) {
204 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
205 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
206 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
207 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
208 Core::Crypto::Op::Decrypt);
209 } else {
210 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
211 }
212
80 for (std::ptrdiff_t i = 0; i < number_sections; ++i) { 213 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
81 // Seek to beginning of this section. 214 auto section = sections[i];
82 NCASectionHeaderBlock block{};
83 if (sizeof(NCASectionHeaderBlock) !=
84 file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
85 LOG_CRITICAL(Loader, "File reader errored out during header read.");
86
87 if (block.filesystem_type == NCASectionFilesystemType::ROMFS) {
88 RomFSSuperblock sb{};
89 if (sizeof(RomFSSuperblock) !=
90 file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE))
91 LOG_CRITICAL(Loader, "File reader errored out during header read.");
92 215
216 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
93 const size_t romfs_offset = 217 const size_t romfs_offset =
94 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + 218 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
95 sb.levels[IVFC_MAX_LEVEL - 1].offset; 219 section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
96 const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; 220 const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
97 files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); 221 auto dec =
98 romfs = files.back(); 222 Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset),
99 } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { 223 romfs_offset);
100 PFS0Superblock sb{}; 224 if (dec != nullptr) {
101 // Seek back to beginning of this section. 225 files.push_back(std::move(dec));
102 if (sizeof(PFS0Superblock) != 226 romfs = files.back();
103 file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) 227 } else {
104 LOG_CRITICAL(Loader, "File reader errored out during header read."); 228 status = Loader::ResultStatus::ErrorMissingKeys;
105 229 return;
230 }
231 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
106 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * 232 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
107 MEDIA_OFFSET_MULTIPLIER) + 233 MEDIA_OFFSET_MULTIPLIER) +
108 sb.pfs0_header_offset; 234 section.pfs0.pfs0_header_offset;
109 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - 235 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
110 header.section_tables[i].media_offset); 236 header.section_tables[i].media_offset);
111 auto npfs = std::make_shared<PartitionFilesystem>( 237 auto dec =
112 std::make_shared<OffsetVfsFile>(file, size, offset)); 238 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
239 if (dec != nullptr) {
240 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
113 241
114 if (npfs->GetStatus() == Loader::ResultStatus::Success) { 242 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
115 dirs.emplace_back(npfs); 243 dirs.push_back(std::move(npfs));
116 if (IsDirectoryExeFS(dirs.back())) 244 if (IsDirectoryExeFS(dirs.back()))
117 exefs = dirs.back(); 245 exefs = dirs.back();
246 }
247 } else {
248 status = Loader::ResultStatus::ErrorMissingKeys;
249 return;
118 } 250 }
119 } 251 }
120 } 252 }
@@ -164,6 +296,10 @@ VirtualDir NCA::GetExeFS() const {
164 return exefs; 296 return exefs;
165} 297}
166 298
299VirtualFile NCA::GetBaseFile() const {
300 return file;
301}
302
167bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 303bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
168 return false; 304 return false;
169} 305}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 0b8b9db61..a8879d9a8 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -8,14 +8,18 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10#include <vector> 10#include <vector>
11 11#include <boost/optional.hpp>
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/swap.h" 14#include "common/swap.h"
15#include "core/crypto/key_manager.h"
15#include "core/file_sys/partition_filesystem.h" 16#include "core/file_sys/partition_filesystem.h"
17#include "core/loader/loader.h"
16 18
17namespace FileSys { 19namespace FileSys {
18 20
21union NCASectionHeader;
22
19enum class NCAContentType : u8 { 23enum class NCAContentType : u8 {
20 Program = 0, 24 Program = 0,
21 Meta = 1, 25 Meta = 1,
@@ -24,6 +28,13 @@ enum class NCAContentType : u8 {
24 Data = 4, 28 Data = 4,
25}; 29};
26 30
31enum class NCASectionCryptoType : u8 {
32 NONE = 1,
33 XTS = 2,
34 CTR = 3,
35 BKTR = 4,
36};
37
27struct NCASectionTableEntry { 38struct NCASectionTableEntry {
28 u32_le media_offset; 39 u32_le media_offset;
29 u32_le media_end_offset; 40 u32_le media_end_offset;
@@ -48,7 +59,7 @@ struct NCAHeader {
48 std::array<u8, 0x10> rights_id; 59 std::array<u8, 0x10> rights_id;
49 std::array<NCASectionTableEntry, 0x4> section_tables; 60 std::array<NCASectionTableEntry, 0x4> section_tables;
50 std::array<std::array<u8, 0x20>, 0x4> hash_tables; 61 std::array<std::array<u8, 0x20>, 0x4> hash_tables;
51 std::array<std::array<u8, 0x10>, 0x4> key_area; 62 std::array<u8, 0x40> key_area;
52 INSERT_PADDING_BYTES(0xC0); 63 INSERT_PADDING_BYTES(0xC0);
53}; 64};
54static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); 65static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size.");
@@ -58,10 +69,7 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
58 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; 69 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
59} 70}
60 71
61inline bool IsValidNCA(const NCAHeader& header) { 72bool IsValidNCA(const NCAHeader& header);
62 return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
63 header.magic == Common::MakeMagic('N', 'C', 'A', '3');
64}
65 73
66// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. 74// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
67// After construction, use GetStatus to determine if the file is valid and ready to be used. 75// After construction, use GetStatus to determine if the file is valid and ready to be used.
@@ -81,10 +89,17 @@ public:
81 VirtualFile GetRomFS() const; 89 VirtualFile GetRomFS() const;
82 VirtualDir GetExeFS() const; 90 VirtualDir GetExeFS() const;
83 91
92 VirtualFile GetBaseFile() const;
93
84protected: 94protected:
85 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 95 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
86 96
87private: 97private:
98 u8 GetCryptoRevision() const;
99 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
100 boost::optional<Core::Crypto::Key128> GetTitlekey() const;
101 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const;
102
88 std::vector<VirtualDir> dirs; 103 std::vector<VirtualDir> dirs;
89 std::vector<VirtualFile> files; 104 std::vector<VirtualFile> files;
90 105
@@ -95,6 +110,10 @@ private:
95 NCAHeader header{}; 110 NCAHeader header{};
96 111
97 Loader::ResultStatus status{}; 112 Loader::ResultStatus status{};
113
114 bool encrypted;
115
116 Core::Crypto::KeyManager keys;
98}; 117};
99 118
100} // namespace FileSys 119} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 521e21078..47e032b19 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -97,9 +97,8 @@ void PartitionFilesystem::PrintDebugInfo() const {
97 LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic); 97 LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic);
98 LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); 98 LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries);
99 for (u32 i = 0; i < pfs_header.num_entries; i++) { 99 for (u32 i = 0; i < pfs_header.num_entries; i++) {
100 LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, 100 LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes)", i,
101 pfs_files[i]->GetName(), pfs_files[i]->GetSize(), 101 pfs_files[i]->GetName(), pfs_files[i]->GetSize());
102 dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset());
103 } 102 }
104} 103}
105 104
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
new file mode 100644
index 000000000..ff3ddb29c
--- /dev/null
+++ b/src/core/file_sys/romfs.cpp
@@ -0,0 +1,124 @@
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 "common/common_types.h"
6#include "common/swap.h"
7#include "core/file_sys/romfs.h"
8#include "core/file_sys/vfs.h"
9#include "core/file_sys/vfs_offset.h"
10#include "core/file_sys/vfs_vector.h"
11
12namespace FileSys {
13
14constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
15
16struct TableLocation {
17 u64_le offset;
18 u64_le size;
19};
20static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size.");
21
22struct RomFSHeader {
23 u64_le header_size;
24 TableLocation directory_hash;
25 TableLocation directory_meta;
26 TableLocation file_hash;
27 TableLocation file_meta;
28 u64_le data_offset;
29};
30static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
31
32struct DirectoryEntry {
33 u32_le sibling;
34 u32_le child_dir;
35 u32_le child_file;
36 u32_le hash;
37 u32_le name_length;
38};
39static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
40
41struct FileEntry {
42 u32_le parent;
43 u32_le sibling;
44 u64_le offset;
45 u64_le size;
46 u32_le hash;
47 u32_le name_length;
48};
49static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
50
51template <typename Entry>
52static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) {
53 Entry entry{};
54 if (file->ReadObject(&entry, offset) != sizeof(Entry))
55 return {};
56 std::string string(entry.name_length, '\0');
57 if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size())
58 return {};
59 return {entry, string};
60}
61
62void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset,
63 std::shared_ptr<VectorVfsDirectory> parent) {
64 while (true) {
65 auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
66
67 parent->AddFile(std::make_shared<OffsetVfsFile>(
68 file, entry.first.size, entry.first.offset + data_offset, entry.second, parent));
69
70 if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
71 break;
72
73 this_file_offset = entry.first.sibling;
74 }
75}
76
77void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset,
78 u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) {
79 while (true) {
80 auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
81 auto current = std::make_shared<VectorVfsDirectory>(
82 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second);
83
84 if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
85 ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
86 }
87
88 if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
89 ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir,
90 current);
91 }
92
93 parent->AddDirectory(current);
94 if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
95 break;
96 this_dir_offset = entry.first.sibling;
97 }
98}
99
100VirtualDir ExtractRomFS(VirtualFile file) {
101 RomFSHeader header{};
102 if (file->ReadObject(&header) != sizeof(RomFSHeader))
103 return nullptr;
104
105 if (header.header_size != sizeof(RomFSHeader))
106 return nullptr;
107
108 const u64 file_offset = header.file_meta.offset;
109 const u64 dir_offset = header.directory_meta.offset + 4;
110
111 const auto root =
112 std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
113 file->GetContainingDirectory(), file->GetName());
114
115 ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
116
117 VirtualDir out = std::move(root);
118
119 while (out->GetSubdirectory("") != nullptr)
120 out = out->GetSubdirectory("");
121
122 return out;
123}
124} // namespace FileSys
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
new file mode 100644
index 000000000..03a876d22
--- /dev/null
+++ b/src/core/file_sys/romfs.h
@@ -0,0 +1,35 @@
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/swap.h"
10#include "core/file_sys/vfs.h"
11
12namespace FileSys {
13
14struct IVFCLevel {
15 u64_le offset;
16 u64_le size;
17 u32_le block_size;
18 u32_le reserved;
19};
20static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
21
22struct IVFCHeader {
23 u32_le magic;
24 u32_le magic_number;
25 INSERT_PADDING_BYTES(8);
26 std::array<IVFCLevel, 6> levels;
27 INSERT_PADDING_BYTES(64);
28};
29static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
30
31// Converts a RomFS binary blob to VFS Filesystem
32// Returns nullptr on failure
33VirtualDir ExtractRomFS(VirtualFile file);
34
35} // namespace FileSys
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index b99a4fd5b..dae1c16ef 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -46,6 +46,13 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
46 return Write(data.data(), data.size(), offset); 46 return Write(data.data(), data.size(), offset);
47} 47}
48 48
49std::string VfsFile::GetFullPath() const {
50 if (GetContainingDirectory() == nullptr)
51 return "/" + GetName();
52
53 return GetContainingDirectory()->GetFullPath() + "/" + GetName();
54}
55
49std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { 56std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const {
50 auto vec = FileUtil::SplitPathComponents(path); 57 auto vec = FileUtil::SplitPathComponents(path);
51 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), 58 vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }),
@@ -243,6 +250,13 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
243 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); 250 return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
244} 251}
245 252
253std::string VfsDirectory::GetFullPath() const {
254 if (IsRoot())
255 return GetName();
256
257 return GetParentDirectory()->GetFullPath() + "/" + GetName();
258}
259
246bool ReadOnlyVfsDirectory::IsWritable() const { 260bool ReadOnlyVfsDirectory::IsWritable() const {
247 return false; 261 return false;
248} 262}
@@ -270,4 +284,33 @@ bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
270bool ReadOnlyVfsDirectory::Rename(std::string_view name) { 284bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
271 return false; 285 return false;
272} 286}
287
288bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) {
289 if (file1->GetSize() != file2->GetSize())
290 return false;
291
292 std::vector<u8> f1_v(block_size);
293 std::vector<u8> f2_v(block_size);
294 for (size_t i = 0; i < file1->GetSize(); i += block_size) {
295 auto f1_vs = file1->Read(f1_v.data(), block_size, i);
296 auto f2_vs = file2->Read(f2_v.data(), block_size, i);
297
298 if (f1_vs != f2_vs)
299 return false;
300 auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end());
301 if (iters.first != f1_v.end() && iters.second != f2_v.end())
302 return false;
303 }
304
305 return true;
306}
307
308bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
309 if (src == nullptr || dest == nullptr)
310 return false;
311 if (!dest->Resize(src->GetSize()))
312 return false;
313 std::vector<u8> data = src->ReadAllBytes();
314 return dest->WriteBytes(data, 0) == data.size();
315}
273} // namespace FileSys 316} // namespace FileSys
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 4a13b8378..fab9e2b45 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -113,6 +113,9 @@ struct VfsFile : NonCopyable {
113 113
114 // Renames the file to name. Returns whether or not the operation was successsful. 114 // Renames the file to name. Returns whether or not the operation was successsful.
115 virtual bool Rename(std::string_view name) = 0; 115 virtual bool Rename(std::string_view name) = 0;
116
117 // Returns the full path of this file as a string, recursively
118 virtual std::string GetFullPath() const;
116}; 119};
117 120
118// A class representing a directory in an abstract filesystem. 121// A class representing a directory in an abstract filesystem.
@@ -213,6 +216,17 @@ struct VfsDirectory : NonCopyable {
213 return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p)); 216 return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
214 } 217 }
215 218
219 bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
220 const std::string& file) {
221 auto file_p = GetFile(file);
222 if (file_p == nullptr)
223 return false;
224 return ReplaceFileWithSubdirectory(file_p, function(file_p));
225 }
226
227 // Returns the full path of this directory as a string, recursively
228 virtual std::string GetFullPath() const;
229
216protected: 230protected:
217 // Backend for InterpretAsDirectory. 231 // Backend for InterpretAsDirectory.
218 // Removes all references to file and adds a reference to dir in the directory's implementation. 232 // Removes all references to file and adds a reference to dir in the directory's implementation.
@@ -230,4 +244,13 @@ struct ReadOnlyVfsDirectory : public VfsDirectory {
230 bool DeleteFile(std::string_view name) override; 244 bool DeleteFile(std::string_view name) override;
231 bool Rename(std::string_view name) override; 245 bool Rename(std::string_view name) override;
232}; 246};
247
248// Compare the two files, byte-for-byte, in increments specificed by block_size
249bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200);
250
251// A method that copies the raw data between two different implementations of VirtualFile. If you
252// are using the same implementation, it is probably better to use the Copy method in the parent
253// directory of src/dest.
254bool VfsRawCopy(VirtualFile src, VirtualFile dest);
255
233} // namespace FileSys 256} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index a40331cef..847cde2f5 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -10,8 +10,9 @@
10namespace FileSys { 10namespace FileSys {
11 11
12OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, 12OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
13 std::string name_) 13 std::string name_, VirtualDir parent_)
14 : file(std::move(file_)), offset(offset_), size(size_), name(std::move(name_)) {} 14 : file(file_), offset(offset_), size(size_), name(std::move(name_)),
15 parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
15 16
16std::string OffsetVfsFile::GetName() const { 17std::string OffsetVfsFile::GetName() const {
17 return name.empty() ? file->GetName() : name; 18 return name.empty() ? file->GetName() : name;
@@ -35,7 +36,7 @@ bool OffsetVfsFile::Resize(size_t new_size) {
35} 36}
36 37
37std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const { 38std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const {
38 return file->GetContainingDirectory(); 39 return parent;
39} 40}
40 41
41bool OffsetVfsFile::IsWritable() const { 42bool OffsetVfsFile::IsWritable() const {
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 4f471e3ba..235970dc5 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -17,7 +17,7 @@ namespace FileSys {
17// the size of this wrapper. 17// the size of this wrapper.
18struct OffsetVfsFile : public VfsFile { 18struct OffsetVfsFile : public VfsFile {
19 OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, 19 OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
20 std::string new_name = ""); 20 std::string new_name = "", VirtualDir new_parent = nullptr);
21 21
22 std::string GetName() const override; 22 std::string GetName() const override;
23 size_t GetSize() const override; 23 size_t GetSize() const override;
@@ -44,6 +44,7 @@ private:
44 size_t offset; 44 size_t offset;
45 size_t size; 45 size_t size;
46 std::string name; 46 std::string name;
47 VirtualDir parent;
47}; 48};
48 49
49} // namespace FileSys 50} // namespace FileSys
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 9ce2e1efa..82d54da4a 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -195,6 +195,12 @@ bool RealVfsDirectory::Rename(std::string_view name) {
195 return FileUtil::Rename(path, new_name); 195 return FileUtil::Rename(path, new_name);
196} 196}
197 197
198std::string RealVfsDirectory::GetFullPath() const {
199 auto out = path;
200 std::replace(out.begin(), out.end(), '\\', '/');
201 return out;
202}
203
198bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 204bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
199 const auto iter = std::find(files.begin(), files.end(), file); 205 const auto iter = std::find(files.begin(), files.end(), file);
200 if (iter == files.end()) 206 if (iter == files.end())
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 2151211c9..243d58576 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -41,7 +41,7 @@ private:
41 41
42// An implementation of VfsDirectory that represents a directory on the user's computer. 42// An implementation of VfsDirectory that represents a directory on the user's computer.
43struct RealVfsDirectory : public VfsDirectory { 43struct RealVfsDirectory : public VfsDirectory {
44 RealVfsDirectory(const std::string& path, Mode perms); 44 RealVfsDirectory(const std::string& path, Mode perms = Mode::Read);
45 45
46 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 46 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
47 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; 47 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
@@ -54,6 +54,7 @@ struct RealVfsDirectory : public VfsDirectory {
54 bool DeleteSubdirectory(std::string_view name) override; 54 bool DeleteSubdirectory(std::string_view name) override;
55 bool DeleteFile(std::string_view name) override; 55 bool DeleteFile(std::string_view name) override;
56 bool Rename(std::string_view name) override; 56 bool Rename(std::string_view name) override;
57 std::string GetFullPath() const override;
57 58
58protected: 59protected:
59 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 60 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
new file mode 100644
index 000000000..fda603960
--- /dev/null
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -0,0 +1,86 @@
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 <utility>
7#include "core/file_sys/vfs_vector.h"
8
9namespace FileSys {
10VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
11 std::vector<VirtualDir> dirs_, VirtualDir parent_,
12 std::string name_)
13 : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
14 name(std::move(name_)) {}
15
16std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
17 return files;
18}
19
20std::vector<std::shared_ptr<VfsDirectory>> VectorVfsDirectory::GetSubdirectories() const {
21 return dirs;
22}
23
24bool VectorVfsDirectory::IsWritable() const {
25 return false;
26}
27
28bool VectorVfsDirectory::IsReadable() const {
29 return true;
30}
31
32std::string VectorVfsDirectory::GetName() const {
33 return name;
34}
35
36std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const {
37 return parent;
38}
39
40template <typename T>
41static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) {
42 const auto iter =
43 std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; });
44 if (iter == vec.end())
45 return false;
46
47 vec.erase(iter);
48 return true;
49}
50
51bool VectorVfsDirectory::DeleteSubdirectory(std::string_view name) {
52 return FindAndRemoveVectorElement(dirs, name);
53}
54
55bool VectorVfsDirectory::DeleteFile(std::string_view name) {
56 return FindAndRemoveVectorElement(files, name);
57}
58
59bool VectorVfsDirectory::Rename(std::string_view name_) {
60 name = name_;
61 return true;
62}
63
64std::shared_ptr<VfsDirectory> VectorVfsDirectory::CreateSubdirectory(std::string_view name) {
65 return nullptr;
66}
67
68std::shared_ptr<VfsFile> VectorVfsDirectory::CreateFile(std::string_view name) {
69 return nullptr;
70}
71
72void VectorVfsDirectory::AddFile(VirtualFile file) {
73 files.push_back(std::move(file));
74}
75
76void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
77 dirs.push_back(std::move(dir));
78}
79
80bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
81 if (!DeleteFile(file->GetName()))
82 return false;
83 dirs.emplace_back(std::move(dir));
84 return true;
85}
86} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
new file mode 100644
index 000000000..ba469647b
--- /dev/null
+++ b/src/core/file_sys/vfs_vector.h
@@ -0,0 +1,44 @@
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/file_sys/vfs.h"
8
9namespace FileSys {
10
11// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
12// Vector data is supplied upon construction.
13struct VectorVfsDirectory : public VfsDirectory {
14 explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
15 std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr,
16 std::string name = "");
17
18 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
19 std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
20 bool IsWritable() const override;
21 bool IsReadable() const override;
22 std::string GetName() const override;
23 std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
24 bool DeleteSubdirectory(std::string_view name) override;
25 bool DeleteFile(std::string_view name) override;
26 bool Rename(std::string_view name) override;
27 std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
28 std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
29
30 virtual void AddFile(VirtualFile file);
31 virtual void AddDirectory(VirtualDir dir);
32
33protected:
34 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
35
36private:
37 std::vector<VirtualFile> files;
38 std::vector<VirtualDir> dirs;
39
40 VirtualDir parent;
41 std::string name;
42};
43
44} // namespace FileSys
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 5ca573652..332e5c3d0 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -37,45 +37,46 @@
37#include "core/core.h" 37#include "core/core.h"
38#include "core/core_cpu.h" 38#include "core/core_cpu.h"
39#include "core/gdbstub/gdbstub.h" 39#include "core/gdbstub/gdbstub.h"
40#include "core/hle/kernel/kernel.h"
41#include "core/hle/kernel/scheduler.h" 40#include "core/hle/kernel/scheduler.h"
42#include "core/loader/loader.h" 41#include "core/loader/loader.h"
43#include "core/memory.h" 42#include "core/memory.h"
44 43
45const int GDB_BUFFER_SIZE = 10000; 44namespace GDBStub {
45namespace {
46constexpr int GDB_BUFFER_SIZE = 10000;
46 47
47const char GDB_STUB_START = '$'; 48constexpr char GDB_STUB_START = '$';
48const char GDB_STUB_END = '#'; 49constexpr char GDB_STUB_END = '#';
49const char GDB_STUB_ACK = '+'; 50constexpr char GDB_STUB_ACK = '+';
50const char GDB_STUB_NACK = '-'; 51constexpr char GDB_STUB_NACK = '-';
51 52
52#ifndef SIGTRAP 53#ifndef SIGTRAP
53const u32 SIGTRAP = 5; 54constexpr u32 SIGTRAP = 5;
54#endif 55#endif
55 56
56#ifndef SIGTERM 57#ifndef SIGTERM
57const u32 SIGTERM = 15; 58constexpr u32 SIGTERM = 15;
58#endif 59#endif
59 60
60#ifndef MSG_WAITALL 61#ifndef MSG_WAITALL
61const u32 MSG_WAITALL = 8; 62constexpr u32 MSG_WAITALL = 8;
62#endif 63#endif
63 64
64const u32 LR_REGISTER = 30; 65constexpr u32 LR_REGISTER = 30;
65const u32 SP_REGISTER = 31; 66constexpr u32 SP_REGISTER = 31;
66const u32 PC_REGISTER = 32; 67constexpr u32 PC_REGISTER = 32;
67const u32 CPSR_REGISTER = 33; 68constexpr u32 CPSR_REGISTER = 33;
68const u32 UC_ARM64_REG_Q0 = 34; 69constexpr u32 UC_ARM64_REG_Q0 = 34;
69const u32 FPSCR_REGISTER = 66; 70constexpr u32 FPSCR_REGISTER = 66;
70 71
71// TODO/WiP - Used while working on support for FPU 72// TODO/WiP - Used while working on support for FPU
72const u32 TODO_DUMMY_REG_997 = 997; 73constexpr u32 TODO_DUMMY_REG_997 = 997;
73const u32 TODO_DUMMY_REG_998 = 998; 74constexpr u32 TODO_DUMMY_REG_998 = 998;
74 75
75// For sample XML files see the GDB source /gdb/features 76// For sample XML files see the GDB source /gdb/features
76// GDB also wants the l character at the start 77// GDB also wants the l character at the start
77// This XML defines what the registers are for this specific ARM device 78// This XML defines what the registers are for this specific ARM device
78static const char* target_xml = 79constexpr char target_xml[] =
79 R"(l<?xml version="1.0"?> 80 R"(l<?xml version="1.0"?>
80<!DOCTYPE target SYSTEM "gdb-target.dtd"> 81<!DOCTYPE target SYSTEM "gdb-target.dtd">
81<target version="1.0"> 82<target version="1.0">
@@ -141,30 +142,28 @@ static const char* target_xml =
141</target> 142</target>
142)"; 143)";
143 144
144namespace GDBStub { 145int gdbserver_socket = -1;
145
146static int gdbserver_socket = -1;
147 146
148static u8 command_buffer[GDB_BUFFER_SIZE]; 147u8 command_buffer[GDB_BUFFER_SIZE];
149static u32 command_length; 148u32 command_length;
150 149
151static u32 latest_signal = 0; 150u32 latest_signal = 0;
152static bool memory_break = false; 151bool memory_break = false;
153 152
154static Kernel::Thread* current_thread = nullptr; 153Kernel::Thread* current_thread = nullptr;
155static u32 current_core = 0; 154u32 current_core = 0;
156 155
157// Binding to a port within the reserved ports range (0-1023) requires root permissions, 156// Binding to a port within the reserved ports range (0-1023) requires root permissions,
158// so default to a port outside of that range. 157// so default to a port outside of that range.
159static u16 gdbstub_port = 24689; 158u16 gdbstub_port = 24689;
160 159
161static bool halt_loop = true; 160bool halt_loop = true;
162static bool step_loop = false; 161bool step_loop = false;
163static bool send_trap = false; 162bool send_trap = false;
164 163
165// If set to false, the server will never be started and no 164// If set to false, the server will never be started and no
166// gdbstub-related functions will be executed. 165// gdbstub-related functions will be executed.
167static std::atomic<bool> server_enabled(false); 166std::atomic<bool> server_enabled(false);
168 167
169#ifdef _WIN32 168#ifdef _WIN32
170WSADATA InitData; 169WSADATA InitData;
@@ -172,23 +171,26 @@ WSADATA InitData;
172 171
173struct Breakpoint { 172struct Breakpoint {
174 bool active; 173 bool active;
175 PAddr addr; 174 VAddr addr;
176 u64 len; 175 u64 len;
176 std::array<u8, 4> inst;
177}; 177};
178 178
179static std::map<u64, Breakpoint> breakpoints_execute; 179using BreakpointMap = std::map<VAddr, Breakpoint>;
180static std::map<u64, Breakpoint> breakpoints_read; 180BreakpointMap breakpoints_execute;
181static std::map<u64, Breakpoint> breakpoints_write; 181BreakpointMap breakpoints_read;
182BreakpointMap breakpoints_write;
182 183
183struct Module { 184struct Module {
184 std::string name; 185 std::string name;
185 PAddr beg; 186 VAddr beg;
186 PAddr end; 187 VAddr end;
187}; 188};
188 189
189static std::vector<Module> modules; 190std::vector<Module> modules;
191} // Anonymous namespace
190 192
191void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) { 193void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
192 Module module; 194 Module module;
193 if (add_elf_ext) { 195 if (add_elf_ext) {
194 Common::SplitPath(name, nullptr, &module.name, nullptr); 196 Common::SplitPath(name, nullptr, &module.name, nullptr);
@@ -419,11 +421,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
419} 421}
420 422
421/** 423/**
422 * Get the list of breakpoints for a given breakpoint type. 424 * Get the map of breakpoints for a given breakpoint type.
423 * 425 *
424 * @param type Type of breakpoint list. 426 * @param type Type of breakpoint map.
425 */ 427 */
426static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { 428static BreakpointMap& GetBreakpointMap(BreakpointType type) {
427 switch (type) { 429 switch (type) {
428 case BreakpointType::Execute: 430 case BreakpointType::Execute:
429 return breakpoints_execute; 431 return breakpoints_execute;
@@ -442,20 +444,24 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
442 * @param type Type of breakpoint. 444 * @param type Type of breakpoint.
443 * @param addr Address of breakpoint. 445 * @param addr Address of breakpoint.
444 */ 446 */
445static void RemoveBreakpoint(BreakpointType type, PAddr addr) { 447static void RemoveBreakpoint(BreakpointType type, VAddr addr) {
446 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 448 BreakpointMap& p = GetBreakpointMap(type);
447 449
448 auto bp = p.find(static_cast<u64>(addr)); 450 const auto bp = p.find(addr);
449 if (bp != p.end()) { 451 if (bp == p.end()) {
450 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", 452 return;
451 bp->second.len, bp->second.addr, static_cast<int>(type));
452 p.erase(static_cast<u64>(addr));
453 } 453 }
454
455 LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}",
456 bp->second.len, bp->second.addr, static_cast<int>(type));
457 Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size());
458 Core::System::GetInstance().InvalidateCpuInstructionCaches();
459 p.erase(addr);
454} 460}
455 461
456BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { 462BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) {
457 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 463 const BreakpointMap& p = GetBreakpointMap(type);
458 auto next_breakpoint = p.lower_bound(static_cast<u64>(addr)); 464 const auto next_breakpoint = p.lower_bound(addr);
459 BreakpointAddress breakpoint; 465 BreakpointAddress breakpoint;
460 466
461 if (next_breakpoint != p.end()) { 467 if (next_breakpoint != p.end()) {
@@ -469,36 +475,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type)
469 return breakpoint; 475 return breakpoint;
470} 476}
471 477
472bool CheckBreakpoint(PAddr addr, BreakpointType type) { 478bool CheckBreakpoint(VAddr addr, BreakpointType type) {
473 if (!IsConnected()) { 479 if (!IsConnected()) {
474 return false; 480 return false;
475 } 481 }
476 482
477 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 483 const BreakpointMap& p = GetBreakpointMap(type);
484 const auto bp = p.find(addr);
478 485
479 auto bp = p.find(static_cast<u64>(addr)); 486 if (bp == p.end()) {
480 if (bp != p.end()) { 487 return false;
481 u64 len = bp->second.len; 488 }
482 489
483 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints 490 u64 len = bp->second.len;
484 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
485 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
486 // two instructions instead of the single instruction you placed the breakpoint
487 // on. So, as a way to make sure that execution breakpoints are only breaking
488 // on the instruction that was specified, set the length of an execution
489 // breakpoint to 1. This should be fine since the CPU should never begin executing
490 // an instruction anywhere except the beginning of the instruction.
491 if (type == BreakpointType::Execute) {
492 len = 1;
493 }
494 491
495 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { 492 // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
496 LOG_DEBUG(Debug_GDBStub, 493 // no matter if it's a 4-byte or 2-byte instruction. When you execute a
497 "Found breakpoint type {} @ {:016X}, range: {:016X}" 494 // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
498 " - {:016X} ({:X} bytes)", 495 // two instructions instead of the single instruction you placed the breakpoint
499 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); 496 // on. So, as a way to make sure that execution breakpoints are only breaking
500 return true; 497 // on the instruction that was specified, set the length of an execution
501 } 498 // breakpoint to 1. This should be fine since the CPU should never begin executing
499 // an instruction anywhere except the beginning of the instruction.
500 if (type == BreakpointType::Execute) {
501 len = 1;
502 }
503
504 if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
505 LOG_DEBUG(Debug_GDBStub,
506 "Found breakpoint type {} @ {:016X}, range: {:016X}"
507 " - {:016X} ({:X} bytes)",
508 static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
509 return true;
502 } 510 }
503 511
504 return false; 512 return false;
@@ -932,6 +940,7 @@ static void WriteMemory() {
932 940
933 GdbHexToMem(data.data(), len_pos + 1, len); 941 GdbHexToMem(data.data(), len_pos + 1, len);
934 Memory::WriteBlock(addr, data.data(), len); 942 Memory::WriteBlock(addr, data.data(), len);
943 Core::System::GetInstance().InvalidateCpuInstructionCaches();
935 SendReply("OK"); 944 SendReply("OK");
936} 945}
937 946
@@ -951,6 +960,7 @@ static void Step() {
951 step_loop = true; 960 step_loop = true;
952 halt_loop = true; 961 halt_loop = true;
953 send_trap = true; 962 send_trap = true;
963 Core::System::GetInstance().InvalidateCpuInstructionCaches();
954} 964}
955 965
956/// Tell the CPU if we hit a memory breakpoint. 966/// Tell the CPU if we hit a memory breakpoint.
@@ -967,6 +977,7 @@ static void Continue() {
967 memory_break = false; 977 memory_break = false;
968 step_loop = false; 978 step_loop = false;
969 halt_loop = false; 979 halt_loop = false;
980 Core::System::GetInstance().InvalidateCpuInstructionCaches();
970} 981}
971 982
972/** 983/**
@@ -976,13 +987,17 @@ static void Continue() {
976 * @param addr Address of breakpoint. 987 * @param addr Address of breakpoint.
977 * @param len Length of breakpoint. 988 * @param len Length of breakpoint.
978 */ 989 */
979static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { 990static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) {
980 std::map<u64, Breakpoint>& p = GetBreakpointList(type); 991 BreakpointMap& p = GetBreakpointMap(type);
981 992
982 Breakpoint breakpoint; 993 Breakpoint breakpoint;
983 breakpoint.active = true; 994 breakpoint.active = true;
984 breakpoint.addr = addr; 995 breakpoint.addr = addr;
985 breakpoint.len = len; 996 breakpoint.len = len;
997 Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size());
998 static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}};
999 Memory::WriteBlock(addr, btrap.data(), btrap.size());
1000 Core::System::GetInstance().InvalidateCpuInstructionCaches();
986 p.insert({addr, breakpoint}); 1001 p.insert({addr, breakpoint});
987 1002
988 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", 1003 LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}",
@@ -1016,7 +1031,7 @@ static void AddBreakpoint() {
1016 1031
1017 auto start_offset = command_buffer + 3; 1032 auto start_offset = command_buffer + 3;
1018 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 1033 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1019 PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); 1034 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1020 1035
1021 start_offset = addr_pos + 1; 1036 start_offset = addr_pos + 1;
1022 u64 len = 1037 u64 len =
@@ -1065,7 +1080,7 @@ static void RemoveBreakpoint() {
1065 1080
1066 auto start_offset = command_buffer + 3; 1081 auto start_offset = command_buffer + 3;
1067 auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); 1082 auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
1068 PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); 1083 VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
1069 1084
1070 if (type == BreakpointType::Access) { 1085 if (type == BreakpointType::Access) {
1071 // Access is made up of Read and Write types, so add both breakpoints 1086 // Access is made up of Read and Write types, so add both breakpoints
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
index a6b50c26c..5a36524b2 100644
--- a/src/core/gdbstub/gdbstub.h
+++ b/src/core/gdbstub/gdbstub.h
@@ -22,7 +22,7 @@ enum class BreakpointType {
22}; 22};
23 23
24struct BreakpointAddress { 24struct BreakpointAddress {
25 PAddr address; 25 VAddr address;
26 BreakpointType type; 26 BreakpointType type;
27}; 27};
28 28
@@ -53,7 +53,7 @@ bool IsServerEnabled();
53bool IsConnected(); 53bool IsConnected();
54 54
55/// Register module. 55/// Register module.
56void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true); 56void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true);
57 57
58/** 58/**
59 * Signal to the gdbstub server that it should halt CPU execution. 59 * Signal to the gdbstub server that it should halt CPU execution.
@@ -74,7 +74,7 @@ void HandlePacket();
74 * @param addr Address to search from. 74 * @param addr Address to search from.
75 * @param type Type of breakpoint. 75 * @param type Type of breakpoint.
76 */ 76 */
77BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); 77BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type);
78 78
79/** 79/**
80 * Check if a breakpoint of the specified type exists at the given address. 80 * Check if a breakpoint of the specified type exists at the given address.
@@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy
82 * @param addr Address of breakpoint. 82 * @param addr Address of breakpoint.
83 * @param type Type of breakpoint. 83 * @param type Type of breakpoint.
84 */ 84 */
85bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); 85bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type);
86 86
87/// If set to true, the CPU will halt at the beginning of the next CPU loop. 87/// If set to true, the CPU will halt at the beginning of the next CPU loop.
88bool GetCpuHaltFlag(); 88bool GetCpuHaltFlag();
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 7fb0da408..d3a734831 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -5,15 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <cstring>
9#include <memory>
8#include <tuple> 10#include <tuple>
9#include <type_traits> 11#include <type_traits>
10#include <utility> 12#include <utility>
13#include "common/assert.h"
14#include "common/common_types.h"
11#include "core/hle/ipc.h" 15#include "core/hle/ipc.h"
12#include "core/hle/kernel/client_port.h" 16#include "core/hle/kernel/client_port.h"
13#include "core/hle/kernel/client_session.h" 17#include "core/hle/kernel/client_session.h"
14#include "core/hle/kernel/handle_table.h"
15#include "core/hle/kernel/hle_ipc.h" 18#include "core/hle/kernel/hle_ipc.h"
16#include "core/hle/kernel/kernel.h" 19#include "core/hle/kernel/object.h"
17#include "core/hle/kernel/server_port.h" 20#include "core/hle/kernel/server_port.h"
18 21
19namespace IPC { 22namespace IPC {
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 233fdab25..03a954a9f 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -2,15 +2,17 @@
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 <vector>
7
5#include "common/assert.h" 8#include "common/assert.h"
6#include "common/common_funcs.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "core/core.h" 10#include "core/core.h"
9#include "core/hle/kernel/errors.h" 11#include "core/hle/kernel/errors.h"
10#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/object.h"
11#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/thread.h" 14#include "core/hle/kernel/thread.h"
13#include "core/hle/lock.h" 15#include "core/hle/result.h"
14#include "core/memory.h" 16#include "core/memory.h"
15 17
16namespace Kernel { 18namespace Kernel {
@@ -30,9 +32,8 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) {
30} 32}
31 33
32// Gets the threads waiting on an address. 34// Gets the threads waiting on an address.
33static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, 35static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
34 VAddr address) { 36 const auto RetrieveWaitingThreads =
35 auto RetrieveWaitingThreads =
36 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { 37 [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) {
37 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); 38 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
38 auto& thread_list = scheduler->GetThreadList(); 39 auto& thread_list = scheduler->GetThreadList();
@@ -43,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t
43 } 44 }
44 }; 45 };
45 46
46 // Retrieve a list of all threads that are waiting for this address. 47 // Retrieve all threads that are waiting for this address.
47 RetrieveWaitingThreads(0, waiting_threads, address); 48 std::vector<SharedPtr<Thread>> threads;
48 RetrieveWaitingThreads(1, waiting_threads, address); 49 RetrieveWaitingThreads(0, threads, address);
49 RetrieveWaitingThreads(2, waiting_threads, address); 50 RetrieveWaitingThreads(1, threads, address);
50 RetrieveWaitingThreads(3, waiting_threads, address); 51 RetrieveWaitingThreads(2, threads, address);
52 RetrieveWaitingThreads(3, threads, address);
53
51 // Sort them by priority, such that the highest priority ones come first. 54 // Sort them by priority, such that the highest priority ones come first.
52 std::sort(waiting_threads.begin(), waiting_threads.end(), 55 std::sort(threads.begin(), threads.end(),
53 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { 56 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
54 return lhs->current_priority < rhs->current_priority; 57 return lhs->current_priority < rhs->current_priority;
55 }); 58 });
59
60 return threads;
56} 61}
57 62
58// Wake up num_to_wake (or all) threads in a vector. 63// Wake up num_to_wake (or all) threads in a vector.
@@ -74,9 +79,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
74 79
75// Signals an address being waited on. 80// Signals an address being waited on.
76ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { 81ResultCode SignalToAddress(VAddr address, s32 num_to_wake) {
77 // Get threads waiting on the address. 82 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
78 std::vector<SharedPtr<Thread>> waiting_threads;
79 GetThreadsWaitingOnAddress(waiting_threads, address);
80 83
81 WakeThreads(waiting_threads, num_to_wake); 84 WakeThreads(waiting_threads, num_to_wake);
82 return RESULT_SUCCESS; 85 return RESULT_SUCCESS;
@@ -108,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
108 } 111 }
109 112
110 // Get threads waiting on the address. 113 // Get threads waiting on the address.
111 std::vector<SharedPtr<Thread>> waiting_threads; 114 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
112 GetThreadsWaitingOnAddress(waiting_threads, address);
113 115
114 // Determine the modified value depending on the waiting count. 116 // Determine the modified value depending on the waiting count.
115 s32 updated_value; 117 s32 updated_value;
116 if (waiting_threads.size() == 0) { 118 if (waiting_threads.empty()) {
117 updated_value = value - 1; 119 updated_value = value - 1;
118 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) { 120 } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) {
119 updated_value = value + 1; 121 updated_value = value + 1;
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index f20f3dbc0..e3657b8e9 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -4,7 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/result.h" 7#include "common/common_types.h"
8
9union ResultCode;
8 10
9namespace Kernel { 11namespace Kernel {
10 12
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index fb2b6f7a3..134e41ebc 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -2,19 +2,20 @@
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 <tuple>
6
6#include "core/hle/kernel/client_port.h" 7#include "core/hle/kernel/client_port.h"
7#include "core/hle/kernel/client_session.h" 8#include "core/hle/kernel/client_session.h"
8#include "core/hle/kernel/errors.h" 9#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/hle_ipc.h" 10#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/object.h"
11#include "core/hle/kernel/server_port.h" 12#include "core/hle/kernel/server_port.h"
12#include "core/hle/kernel/server_session.h" 13#include "core/hle/kernel/server_session.h"
13 14
14namespace Kernel { 15namespace Kernel {
15 16
16ClientPort::ClientPort() {} 17ClientPort::ClientPort() = default;
17ClientPort::~ClientPort() {} 18ClientPort::~ClientPort() = default;
18 19
19ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { 20ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
20 // Note: Threads do not wait for the server endpoint to call 21 // Note: Threads do not wait for the server endpoint to call
@@ -39,4 +40,12 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
39 return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); 40 return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
40} 41}
41 42
43void ClientPort::ConnectionClosed() {
44 if (active_sessions == 0) {
45 return;
46 }
47
48 --active_sessions;
49}
50
42} // namespace Kernel 51} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index a829aeb6d..b1269ea5c 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -6,7 +6,7 @@
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/object.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11 11
12namespace Kernel { 12namespace Kernel {
@@ -37,14 +37,20 @@ public:
37 */ 37 */
38 ResultVal<SharedPtr<ClientSession>> Connect(); 38 ResultVal<SharedPtr<ClientSession>> Connect();
39 39
40 SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. 40 /**
41 u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have 41 * Signifies that a previously active connection has been closed,
42 u32 active_sessions; ///< Number of currently open sessions to this port 42 * decreasing the total number of active connections to this port.
43 std::string name; ///< Name of client port (optional) 43 */
44 void ConnectionClosed();
44 45
45private: 46private:
46 ClientPort(); 47 ClientPort();
47 ~ClientPort() override; 48 ~ClientPort() override;
49
50 SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
51 u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have
52 u32 active_sessions = 0; ///< Number of currently open sessions to this port
53 std::string name; ///< Name of client port (optional)
48}; 54};
49 55
50} // namespace Kernel 56} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 72773d8b1..fdffc648d 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -2,8 +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 "common/assert.h"
6
7#include "core/hle/kernel/client_session.h" 5#include "core/hle/kernel/client_session.h"
8#include "core/hle/kernel/errors.h" 6#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/hle_ipc.h" 7#include "core/hle/kernel/hle_ipc.h"
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 2258f95bc..dabd93ed7 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -7,7 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/object.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12 12
13namespace Kernel { 13namespace Kernel {
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 9cae2369f..5623c4b6a 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -3,11 +3,9 @@
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 <map>
7#include <vector>
8#include "common/assert.h" 6#include "common/assert.h"
9#include "core/hle/kernel/event.h" 7#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/object.h"
11#include "core/hle/kernel/thread.h" 9#include "core/hle/kernel/thread.h"
12 10
13namespace Kernel { 11namespace Kernel {
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index e5c924a75..3c20c05e8 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/object.h"
9#include "core/hle/kernel/wait_object.h" 9#include "core/hle/kernel/wait_object.h"
10 10
11namespace Kernel { 11namespace Kernel {
@@ -31,10 +31,9 @@ public:
31 return HANDLE_TYPE; 31 return HANDLE_TYPE;
32 } 32 }
33 33
34 ResetType reset_type; ///< Current ResetType 34 ResetType GetResetType() const {
35 35 return reset_type;
36 bool signaled; ///< Whether the event has already been signaled 36 }
37 std::string name; ///< Name of event (optional)
38 37
39 bool ShouldWait(Thread* thread) const override; 38 bool ShouldWait(Thread* thread) const override;
40 void Acquire(Thread* thread) override; 39 void Acquire(Thread* thread) override;
@@ -47,6 +46,11 @@ public:
47private: 46private:
48 Event(); 47 Event();
49 ~Event() override; 48 ~Event() override;
49
50 ResetType reset_type; ///< Current ResetType
51
52 bool signaled; ///< Whether the event has already been signaled
53 std::string name; ///< Name of event (optional)
50}; 54};
51 55
52} // namespace Kernel 56} // namespace Kernel
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index 7dd67f80f..28e21428a 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -8,7 +8,6 @@
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/kernel/errors.h" 9#include "core/hle/kernel/errors.h"
10#include "core/hle/kernel/handle_table.h" 10#include "core/hle/kernel/handle_table.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
14 13
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index ba968c666..22ddda630 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -7,7 +7,7 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/object.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12 12
13namespace Kernel { 13namespace Kernel {
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index f24392520..5dd1b68d7 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -2,17 +2,22 @@
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 <array>
7#include <sstream>
5#include <utility> 8#include <utility>
6 9
7#include <boost/range/algorithm_ext/erase.hpp> 10#include <boost/range/algorithm_ext/erase.hpp>
11
8#include "common/assert.h" 12#include "common/assert.h"
9#include "common/common_funcs.h" 13#include "common/common_funcs.h"
10#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/logging/log.h"
11#include "core/hle/ipc_helpers.h" 16#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/event.h" 17#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/handle_table.h" 18#include "core/hle/kernel/handle_table.h"
14#include "core/hle/kernel/hle_ipc.h" 19#include "core/hle/kernel/hle_ipc.h"
15#include "core/hle/kernel/kernel.h" 20#include "core/hle/kernel/object.h"
16#include "core/hle/kernel/process.h" 21#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/server_session.h" 22#include "core/hle/kernel/server_session.h"
18#include "core/memory.h" 23#include "core/memory.h"
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 84727f748..9ce52db24 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <iterator>
9#include <memory> 8#include <memory>
10#include <string> 9#include <string>
11#include <type_traits> 10#include <type_traits>
@@ -14,7 +13,7 @@
14#include "common/common_types.h" 13#include "common/common_types.h"
15#include "common/swap.h" 14#include "common/swap.h"
16#include "core/hle/ipc.h" 15#include "core/hle/ipc.h"
17#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/object.h"
18#include "core/hle/kernel/server_session.h" 17#include "core/hle/kernel/server_session.h"
19#include "core/hle/kernel/thread.h" 18#include "core/hle/kernel/thread.h"
20 19
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1beb98566..1b0cd0abf 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -4,8 +4,6 @@
4 4
5#include "core/hle/kernel/handle_table.h" 5#include "core/hle/kernel/handle_table.h"
6#include "core/hle/kernel/kernel.h" 6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/memory.h"
8#include "core/hle/kernel/object_address_table.h"
9#include "core/hle/kernel/process.h" 7#include "core/hle/kernel/process.h"
10#include "core/hle/kernel/resource_limit.h" 8#include "core/hle/kernel/resource_limit.h"
11#include "core/hle/kernel/thread.h" 9#include "core/hle/kernel/thread.h"
@@ -16,9 +14,7 @@ namespace Kernel {
16unsigned int Object::next_object_id; 14unsigned int Object::next_object_id;
17 15
18/// Initialize the kernel 16/// Initialize the kernel
19void Init(u32 system_mode) { 17void Init() {
20 Kernel::MemoryInit(system_mode);
21
22 Kernel::ResourceLimitsInit(); 18 Kernel::ResourceLimitsInit();
23 Kernel::ThreadingInit(); 19 Kernel::ThreadingInit();
24 Kernel::TimersInit(); 20 Kernel::TimersInit();
@@ -33,13 +29,11 @@ void Init(u32 system_mode) {
33void Shutdown() { 29void Shutdown() {
34 // Free all kernel objects 30 // Free all kernel objects
35 g_handle_table.Clear(); 31 g_handle_table.Clear();
36 g_object_address_table.Clear();
37 32
38 Kernel::ThreadingShutdown(); 33 Kernel::ThreadingShutdown();
39 34
40 Kernel::TimersShutdown(); 35 Kernel::TimersShutdown();
41 Kernel::ResourceLimitsShutdown(); 36 Kernel::ResourceLimitsShutdown();
42 Kernel::MemoryShutdown();
43} 37}
44 38
45} // namespace Kernel 39} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 402ae900f..131311472 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,122 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <cstddef>
8#include <string>
9#include <utility>
10#include <boost/smart_ptr/intrusive_ptr.hpp>
11#include "common/assert.h"
12#include "common/common_types.h" 7#include "common/common_types.h"
13 8
14namespace Kernel { 9namespace Kernel {
15 10
16using Handle = u32;
17
18enum class HandleType : u32 {
19 Unknown,
20 Event,
21 SharedMemory,
22 Thread,
23 Process,
24 AddressArbiter,
25 Timer,
26 ResourceLimit,
27 CodeSet,
28 ClientPort,
29 ServerPort,
30 ClientSession,
31 ServerSession,
32};
33
34enum class ResetType {
35 OneShot,
36 Sticky,
37 Pulse,
38};
39
40class Object : NonCopyable {
41public:
42 virtual ~Object() {}
43
44 /// Returns a unique identifier for the object. For debugging purposes only.
45 unsigned int GetObjectId() const {
46 return object_id;
47 }
48
49 virtual std::string GetTypeName() const {
50 return "[BAD KERNEL OBJECT TYPE]";
51 }
52 virtual std::string GetName() const {
53 return "[UNKNOWN KERNEL OBJECT]";
54 }
55 virtual Kernel::HandleType GetHandleType() const = 0;
56
57 /**
58 * Check if a thread can wait on the object
59 * @return True if a thread can wait on the object, otherwise false
60 */
61 bool IsWaitable() const {
62 switch (GetHandleType()) {
63 case HandleType::Event:
64 case HandleType::Thread:
65 case HandleType::Timer:
66 case HandleType::ServerPort:
67 case HandleType::ServerSession:
68 return true;
69
70 case HandleType::Unknown:
71 case HandleType::SharedMemory:
72 case HandleType::Process:
73 case HandleType::AddressArbiter:
74 case HandleType::ResourceLimit:
75 case HandleType::CodeSet:
76 case HandleType::ClientPort:
77 case HandleType::ClientSession:
78 return false;
79 }
80
81 UNREACHABLE();
82 }
83
84public:
85 static unsigned int next_object_id;
86
87private:
88 friend void intrusive_ptr_add_ref(Object*);
89 friend void intrusive_ptr_release(Object*);
90
91 unsigned int ref_count = 0;
92 unsigned int object_id = next_object_id++;
93};
94
95// Special functions used by boost::instrusive_ptr to do automatic ref-counting
96inline void intrusive_ptr_add_ref(Object* object) {
97 ++object->ref_count;
98}
99
100inline void intrusive_ptr_release(Object* object) {
101 if (--object->ref_count == 0) {
102 delete object;
103 }
104}
105
106template <typename T>
107using SharedPtr = boost::intrusive_ptr<T>;
108
109/**
110 * Attempts to downcast the given Object pointer to a pointer to T.
111 * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
112 */
113template <typename T>
114inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
115 if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
116 return boost::static_pointer_cast<T>(std::move(object));
117 }
118 return nullptr;
119}
120
121/// Initialize the kernel with the specified system mode. 11/// Initialize the kernel with the specified system mode.
122void Init(u32 system_mode); 12void Init();
123 13
124/// Shutdown the kernel 14/// Shutdown the kernel
125void Shutdown(); 15void Shutdown();
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
deleted file mode 100644
index 94eac677c..000000000
--- a/src/core/hle/kernel/memory.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
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 <algorithm>
6#include <cinttypes>
7#include <map>
8#include <memory>
9#include <utility>
10#include <vector>
11#include "common/assert.h"
12#include "common/common_types.h"
13#include "common/logging/log.h"
14#include "core/hle/kernel/memory.h"
15#include "core/hle/kernel/vm_manager.h"
16#include "core/hle/result.h"
17#include "core/memory.h"
18#include "core/memory_setup.h"
19
20////////////////////////////////////////////////////////////////////////////////////////////////////
21
22namespace Kernel {
23
24MemoryRegionInfo memory_regions[3];
25
26/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system
27/// memory configuration type.
28static const u32 memory_region_sizes[8][3] = {
29 // Old 3DS layouts
30 {0x04000000, 0x02C00000, 0x01400000}, // 0
31 {/* This appears to be unused. */}, // 1
32 {0x06000000, 0x00C00000, 0x01400000}, // 2
33 {0x05000000, 0x01C00000, 0x01400000}, // 3
34 {0x04800000, 0x02400000, 0x01400000}, // 4
35 {0x02000000, 0x04C00000, 0x01400000}, // 5
36
37 // New 3DS layouts
38 {0x07C00000, 0x06400000, 0x02000000}, // 6
39 {0x0B200000, 0x02E00000, 0x02000000}, // 7
40};
41
42void MemoryInit(u32 mem_type) {
43 // TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead.
44 ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!");
45 ASSERT(mem_type != 1);
46
47 // The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with
48 // the sizes specified in the memory_region_sizes table.
49 VAddr base = 0;
50 for (int i = 0; i < 3; ++i) {
51 memory_regions[i].base = base;
52 memory_regions[i].size = memory_region_sizes[mem_type][i];
53 memory_regions[i].used = 0;
54 memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
55 // Reserve enough space for this region of FCRAM.
56 // We do not want this block of memory to be relocated when allocating from it.
57 memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size);
58
59 base += memory_regions[i].size;
60 }
61
62 // We must've allocated the entire FCRAM by the end
63 ASSERT(base == Memory::FCRAM_SIZE);
64}
65
66void MemoryShutdown() {
67 for (auto& region : memory_regions) {
68 region.base = 0;
69 region.size = 0;
70 region.used = 0;
71 region.linear_heap_memory = nullptr;
72 }
73}
74
75MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) {
76 switch (region) {
77 case MemoryRegion::APPLICATION:
78 return &memory_regions[0];
79 case MemoryRegion::SYSTEM:
80 return &memory_regions[1];
81 case MemoryRegion::BASE:
82 return &memory_regions[2];
83 default:
84 UNREACHABLE();
85 }
86}
87
88void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {}
89
90void MapSharedPages(VMManager& address_space) {}
91
92} // namespace Kernel
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h
deleted file mode 100644
index 61e30c679..000000000
--- a/src/core/hle/kernel/memory.h
+++ /dev/null
@@ -1,31 +0,0 @@
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 <memory>
8#include "common/common_types.h"
9#include "core/hle/kernel/process.h"
10
11namespace Kernel {
12
13class VMManager;
14
15struct MemoryRegionInfo {
16 u64 base; // Not an address, but offset from start of FCRAM
17 u64 size;
18 u64 used;
19
20 std::shared_ptr<std::vector<u8>> linear_heap_memory;
21};
22
23void MemoryInit(u32 mem_type);
24void MemoryShutdown();
25MemoryRegionInfo* GetMemoryRegion(MemoryRegion region);
26
27void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
28void MapSharedPages(VMManager& address_space);
29
30extern MemoryRegionInfo memory_regions[3];
31} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index feb7b88d2..cb7f58b35 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -3,16 +3,19 @@
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 <utility>
6#include <vector> 7#include <vector>
8
7#include <boost/range/algorithm_ext/erase.hpp> 9#include <boost/range/algorithm_ext/erase.hpp>
10
8#include "common/assert.h" 11#include "common/assert.h"
9#include "core/core.h" 12#include "core/core.h"
10#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
11#include "core/hle/kernel/handle_table.h" 14#include "core/hle/kernel/handle_table.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/mutex.h" 15#include "core/hle/kernel/mutex.h"
14#include "core/hle/kernel/object_address_table.h" 16#include "core/hle/kernel/object.h"
15#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
18#include "core/hle/result.h"
16 19
17namespace Kernel { 20namespace Kernel {
18 21
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 3117e7c70..45268bbe9 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -4,12 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <string>
8#include "common/common_types.h" 7#include "common/common_types.h"
9#include "common/swap.h" 8#include "core/hle/kernel/object.h"
10#include "core/hle/kernel/kernel.h" 9
11#include "core/hle/kernel/wait_object.h" 10union ResultCode;
12#include "core/hle/result.h"
13 11
14namespace Kernel { 12namespace Kernel {
15 13
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
new file mode 100644
index 000000000..cdba272f5
--- /dev/null
+++ b/src/core/hle/kernel/object.cpp
@@ -0,0 +1,35 @@
1// Copyright 2018 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/hle/kernel/object.h"
7
8namespace Kernel {
9
10Object::~Object() = default;
11
12bool Object::IsWaitable() const {
13 switch (GetHandleType()) {
14 case HandleType::Event:
15 case HandleType::Thread:
16 case HandleType::Timer:
17 case HandleType::ServerPort:
18 case HandleType::ServerSession:
19 return true;
20
21 case HandleType::Unknown:
22 case HandleType::SharedMemory:
23 case HandleType::Process:
24 case HandleType::AddressArbiter:
25 case HandleType::ResourceLimit:
26 case HandleType::CodeSet:
27 case HandleType::ClientPort:
28 case HandleType::ClientSession:
29 return false;
30 }
31
32 UNREACHABLE();
33}
34
35} // namespace Kernel
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
new file mode 100644
index 000000000..83df68dfd
--- /dev/null
+++ b/src/core/hle/kernel/object.h
@@ -0,0 +1,100 @@
1// Copyright 2018 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 <utility>
9
10#include <boost/smart_ptr/intrusive_ptr.hpp>
11
12#include "common/common_types.h"
13
14namespace Kernel {
15
16using Handle = u32;
17
18enum class HandleType : u32 {
19 Unknown,
20 Event,
21 SharedMemory,
22 Thread,
23 Process,
24 AddressArbiter,
25 Timer,
26 ResourceLimit,
27 CodeSet,
28 ClientPort,
29 ServerPort,
30 ClientSession,
31 ServerSession,
32};
33
34enum class ResetType {
35 OneShot,
36 Sticky,
37 Pulse,
38};
39
40class Object : NonCopyable {
41public:
42 virtual ~Object();
43
44 /// Returns a unique identifier for the object. For debugging purposes only.
45 unsigned int GetObjectId() const {
46 return object_id;
47 }
48
49 virtual std::string GetTypeName() const {
50 return "[BAD KERNEL OBJECT TYPE]";
51 }
52 virtual std::string GetName() const {
53 return "[UNKNOWN KERNEL OBJECT]";
54 }
55 virtual HandleType GetHandleType() const = 0;
56
57 /**
58 * Check if a thread can wait on the object
59 * @return True if a thread can wait on the object, otherwise false
60 */
61 bool IsWaitable() const;
62
63public:
64 static unsigned int next_object_id;
65
66private:
67 friend void intrusive_ptr_add_ref(Object*);
68 friend void intrusive_ptr_release(Object*);
69
70 unsigned int ref_count = 0;
71 unsigned int object_id = next_object_id++;
72};
73
74// Special functions used by boost::instrusive_ptr to do automatic ref-counting
75inline void intrusive_ptr_add_ref(Object* object) {
76 ++object->ref_count;
77}
78
79inline void intrusive_ptr_release(Object* object) {
80 if (--object->ref_count == 0) {
81 delete object;
82 }
83}
84
85template <typename T>
86using SharedPtr = boost::intrusive_ptr<T>;
87
88/**
89 * Attempts to downcast the given Object pointer to a pointer to T.
90 * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
91 */
92template <typename T>
93inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
94 if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
95 return boost::static_pointer_cast<T>(std::move(object));
96 }
97 return nullptr;
98}
99
100} // namespace Kernel
diff --git a/src/core/hle/kernel/object_address_table.cpp b/src/core/hle/kernel/object_address_table.cpp
deleted file mode 100644
index ca8a833a1..000000000
--- a/src/core/hle/kernel/object_address_table.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
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 <utility>
6
7#include "common/assert.h"
8#include "core/hle/kernel/object_address_table.h"
9
10namespace Kernel {
11
12ObjectAddressTable g_object_address_table;
13
14void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) {
15 ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x{:X}", addr);
16 objects[addr] = std::move(obj);
17}
18
19void ObjectAddressTable::Close(VAddr addr) {
20 ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x{:X}", addr);
21 objects.erase(addr);
22}
23
24SharedPtr<Object> ObjectAddressTable::GetGeneric(VAddr addr) const {
25 auto iter = objects.find(addr);
26 if (iter != objects.end()) {
27 return iter->second;
28 }
29 return {};
30}
31
32void ObjectAddressTable::Clear() {
33 objects.clear();
34}
35
36} // namespace Kernel
diff --git a/src/core/hle/kernel/object_address_table.h b/src/core/hle/kernel/object_address_table.h
deleted file mode 100644
index a09004b32..000000000
--- a/src/core/hle/kernel/object_address_table.h
+++ /dev/null
@@ -1,62 +0,0 @@
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 <map>
8#include "common/common_types.h"
9#include "core/hle/kernel/kernel.h"
10
11namespace Kernel {
12
13/**
14 * This class is used to keep a table of Kernel objects and their respective addresses in emulated
15 * memory. For certain Switch SVCs, Kernel objects are referenced by an address to an object the
16 * guest application manages, so we use this table to look these kernel objects up. This is similiar
17 * to the HandleTable class.
18 */
19class ObjectAddressTable final : NonCopyable {
20public:
21 ObjectAddressTable() = default;
22
23 /**
24 * Inserts an object and address pair into the table.
25 */
26 void Insert(VAddr addr, SharedPtr<Object> obj);
27
28 /**
29 * Closes an object by its address, removing it from the table and decreasing the object's
30 * ref-count.
31 * @return `RESULT_SUCCESS` or one of the following errors:
32 * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
33 */
34 void Close(VAddr addr);
35
36 /**
37 * Looks up an object by its address.
38 * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
39 */
40 SharedPtr<Object> GetGeneric(VAddr addr) const;
41
42 /**
43 * Looks up an object by its address while verifying its type.
44 * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
45 * type differs from the requested one.
46 */
47 template <class T>
48 SharedPtr<T> Get(VAddr addr) const {
49 return DynamicObjectCast<T>(GetGeneric(addr));
50 }
51
52 /// Closes all addresses held in this table.
53 void Clear();
54
55private:
56 /// Stores the Object referenced by the address
57 std::map<VAddr, SharedPtr<Object>> objects;
58};
59
60extern ObjectAddressTable g_object_address_table;
61
62} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 0c0506085..edf34c5a3 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -8,7 +8,6 @@
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/hle/kernel/errors.h" 10#include "core/hle/kernel/errors.h"
11#include "core/hle/kernel/memory.h"
12#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
13#include "core/hle/kernel/resource_limit.h" 12#include "core/hle/kernel/resource_limit.h"
14#include "core/hle/kernel/thread.h" 13#include "core/hle/kernel/thread.h"
@@ -125,14 +124,6 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
125 std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, 124 std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
126 MemoryState::Mapped) 125 MemoryState::Mapped)
127 .Unwrap(); 126 .Unwrap();
128 misc_memory_used += stack_size;
129 memory_region->used += stack_size;
130
131 // Map special address mappings
132 MapSharedPages(vm_manager);
133 for (const auto& mapping : address_mappings) {
134 HandleSpecialMapping(vm_manager, mapping);
135 }
136 127
137 vm_manager.LogLayout(); 128 vm_manager.LogLayout();
138 status = ProcessStatus::Running; 129 status = ProcessStatus::Running;
@@ -141,37 +132,19 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
141} 132}
142 133
143void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 134void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
144 memory_region = GetMemoryRegion(flags.memory_region); 135 const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
145 136 MemoryState memory_state) {
146 auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
147 MemoryState memory_state) {
148 auto vma = vm_manager 137 auto vma = vm_manager
149 .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, 138 .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
150 segment.size, memory_state) 139 segment.size, memory_state)
151 .Unwrap(); 140 .Unwrap();
152 vm_manager.Reprotect(vma, permissions); 141 vm_manager.Reprotect(vma, permissions);
153 misc_memory_used += segment.size;
154 memory_region->used += segment.size;
155 }; 142 };
156 143
157 // Map CodeSet segments 144 // Map CodeSet segments
158 MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic); 145 MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
159 MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable); 146 MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
160 MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable); 147 MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
161}
162
163VAddr Process::GetLinearHeapAreaAddress() const {
164 // Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of
165 // the extra RAM in the n3DS.
166 return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR;
167}
168
169VAddr Process::GetLinearHeapBase() const {
170 return GetLinearHeapAreaAddress() + memory_region->base;
171}
172
173VAddr Process::GetLinearHeapLimit() const {
174 return GetLinearHeapBase() + memory_region->size;
175} 148}
176 149
177ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 150ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
@@ -206,7 +179,6 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
206 vm_manager.Reprotect(vma, perms); 179 vm_manager.Reprotect(vma, perms);
207 180
208 heap_used = size; 181 heap_used = size;
209 memory_region->used += size;
210 182
211 return MakeResult<VAddr>(heap_end - size); 183 return MakeResult<VAddr>(heap_end - size);
212} 184}
@@ -226,52 +198,6 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
226 return result; 198 return result;
227 199
228 heap_used -= size; 200 heap_used -= size;
229 memory_region->used -= size;
230
231 return RESULT_SUCCESS;
232}
233
234ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) {
235 UNIMPLEMENTED();
236 return {};
237}
238
239ResultCode Process::LinearFree(VAddr target, u32 size) {
240 auto& linheap_memory = memory_region->linear_heap_memory;
241
242 if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() ||
243 target + size < target) {
244
245 return ERR_INVALID_ADDRESS;
246 }
247
248 if (size == 0) {
249 return RESULT_SUCCESS;
250 }
251
252 VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size();
253 if (target + size > heap_end) {
254 return ERR_INVALID_ADDRESS_STATE;
255 }
256
257 ResultCode result = vm_manager.UnmapRange(target, size);
258 if (result.IsError())
259 return result;
260
261 linear_heap_used -= size;
262 memory_region->used -= size;
263
264 if (target + size == heap_end) {
265 // End of linear heap has been freed, so check what's the last allocated block in it and
266 // reduce the size.
267 auto vma = vm_manager.FindVMA(target);
268 ASSERT(vma != vm_manager.vma_map.end());
269 ASSERT(vma->second.type == VMAType::Free);
270 VAddr new_end = vma->second.base;
271 if (new_end >= GetLinearHeapBase()) {
272 linheap_memory->resize(new_end - GetLinearHeapBase());
273 }
274 }
275 201
276 return RESULT_SUCCESS; 202 return RESULT_SUCCESS;
277} 203}
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 68e77a4d1..992689186 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <bitset> 8#include <bitset>
8#include <cstddef> 9#include <cstddef>
9#include <memory> 10#include <memory>
@@ -12,7 +13,7 @@
12#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
13#include "common/bit_field.h" 14#include "common/bit_field.h"
14#include "common/common_types.h" 15#include "common/common_types.h"
15#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/object.h"
16#include "core/hle/kernel/thread.h" 17#include "core/hle/kernel/thread.h"
17#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
18 19
@@ -53,9 +54,14 @@ union ProcessFlags {
53enum class ProcessStatus { Created, Running, Exited }; 54enum class ProcessStatus { Created, Running, Exited };
54 55
55class ResourceLimit; 56class ResourceLimit;
56struct MemoryRegionInfo;
57 57
58struct CodeSet final : public Object { 58struct CodeSet final : public Object {
59 struct Segment {
60 size_t offset = 0;
61 VAddr addr = 0;
62 u32 size = 0;
63 };
64
59 static SharedPtr<CodeSet> Create(std::string name); 65 static SharedPtr<CodeSet> Create(std::string name);
60 66
61 std::string GetTypeName() const override { 67 std::string GetTypeName() const override {
@@ -70,24 +76,38 @@ struct CodeSet final : public Object {
70 return HANDLE_TYPE; 76 return HANDLE_TYPE;
71 } 77 }
72 78
73 /// Name of the process 79 Segment& CodeSegment() {
74 std::string name; 80 return segments[0];
81 }
75 82
76 std::shared_ptr<std::vector<u8>> memory; 83 const Segment& CodeSegment() const {
84 return segments[0];
85 }
77 86
78 struct Segment { 87 Segment& RODataSegment() {
79 size_t offset = 0; 88 return segments[1];
80 VAddr addr = 0; 89 }
81 u32 size = 0; 90
82 }; 91 const Segment& RODataSegment() const {
92 return segments[1];
93 }
94
95 Segment& DataSegment() {
96 return segments[2];
97 }
98
99 const Segment& DataSegment() const {
100 return segments[2];
101 }
83 102
84 Segment segments[3]; 103 std::shared_ptr<std::vector<u8>> memory;
85 Segment& code = segments[0];
86 Segment& rodata = segments[1];
87 Segment& data = segments[2];
88 104
105 std::array<Segment, 3> segments;
89 VAddr entrypoint; 106 VAddr entrypoint;
90 107
108 /// Name of the process
109 std::string name;
110
91private: 111private:
92 CodeSet(); 112 CodeSet();
93 ~CodeSet() override; 113 ~CodeSet() override;
@@ -163,12 +183,11 @@ public:
163 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous 183 // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
164 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. 184 // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
165 std::shared_ptr<std::vector<u8>> heap_memory; 185 std::shared_ptr<std::vector<u8>> heap_memory;
166 // The left/right bounds of the address space covered by heap_memory.
167 VAddr heap_start = 0, heap_end = 0;
168 186
169 u64 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0; 187 // The left/right bounds of the address space covered by heap_memory.
170 188 VAddr heap_start = 0;
171 MemoryRegionInfo* memory_region = nullptr; 189 VAddr heap_end = 0;
190 u64 heap_used = 0;
172 191
173 /// The Thread Local Storage area is allocated as processes create threads, 192 /// The Thread Local Storage area is allocated as processes create threads,
174 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part 193 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
@@ -179,16 +198,9 @@ public:
179 198
180 std::string name; 199 std::string name;
181 200
182 VAddr GetLinearHeapAreaAddress() const;
183 VAddr GetLinearHeapBase() const;
184 VAddr GetLinearHeapLimit() const;
185
186 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); 201 ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
187 ResultCode HeapFree(VAddr target, u32 size); 202 ResultCode HeapFree(VAddr target, u32 size);
188 203
189 ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms);
190 ResultCode LinearFree(VAddr target, u32 size);
191
192 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); 204 ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
193 205
194 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); 206 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index cc689a27a..0fa141db3 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/object.h"
9 9
10namespace Kernel { 10namespace Kernel {
11 11
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index e307eec98..94065c736 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -2,8 +2,12 @@
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 <utility> 6#include <utility>
6 7
8#include "common/assert.h"
9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h"
7#include "core/core.h" 11#include "core/core.h"
8#include "core/core_timing.h" 12#include "core/core_timing.h"
9#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index a3b5fb8ca..1a4ee8f36 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -8,9 +8,11 @@
8#include <vector> 8#include <vector>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/thread_queue_list.h" 10#include "common/thread_queue_list.h"
11#include "core/arm/arm_interface.h" 11#include "core/hle/kernel/object.h"
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
13 13
14class ARM_Interface;
15
14namespace Kernel { 16namespace Kernel {
15 17
16class Scheduler final { 18class Scheduler final {
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index 0b7061403..7b6211fd8 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -6,7 +6,7 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/hle/kernel/client_port.h" 7#include "core/hle/kernel/client_port.h"
8#include "core/hle/kernel/errors.h" 8#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/object.h"
10#include "core/hle/kernel/server_port.h" 10#include "core/hle/kernel/server_port.h"
11#include "core/hle/kernel/server_session.h" 11#include "core/hle/kernel/server_session.h"
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 9ef4ecc35..7f6d6b3eb 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -7,8 +7,9 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include <tuple> 9#include <tuple>
10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/object.h"
12#include "core/hle/kernel/wait_object.h" 13#include "core/hle/kernel/wait_object.h"
13 14
14namespace Kernel { 15namespace Kernel {
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 29b163528..93560152f 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -5,6 +5,8 @@
5#include <tuple> 5#include <tuple>
6#include <utility> 6#include <utility>
7 7
8#include "common/assert.h"
9#include "common/logging/log.h"
8#include "core/core.h" 10#include "core/core.h"
9#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/client_port.h" 12#include "core/hle/kernel/client_port.h"
@@ -25,7 +27,7 @@ ServerSession::~ServerSession() {
25 27
26 // Decrease the port's connection count. 28 // Decrease the port's connection count.
27 if (parent->port) 29 if (parent->port)
28 parent->port->active_sessions--; 30 parent->port->ConnectionClosed();
29 31
30 // TODO(Subv): Wake up all the ClientSession's waiting threads and set 32 // TODO(Subv): Wake up all the ClientSession's waiting threads and set
31 // the SendSyncRequest result to 0xC920181A. 33 // the SendSyncRequest result to 0xC920181A.
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 2da807042..2bce54fee 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -6,12 +6,12 @@
6 6
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/assert.h" 9#include <vector>
10
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/object.h"
12#include "core/hle/kernel/wait_object.h" 13#include "core/hle/kernel/wait_object.h"
13#include "core/hle/result.h" 14#include "core/hle/result.h"
14#include "core/memory.h"
15 15
16namespace Kernel { 16namespace Kernel {
17 17
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h
index e69b034a7..7a551f5e4 100644
--- a/src/core/hle/kernel/session.h
+++ b/src/core/hle/kernel/session.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/kernel/kernel.h" 7#include "core/hle/kernel/object.h"
8 8
9namespace Kernel { 9namespace Kernel {
10 10
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 4bf11c7e2..21ddc2f7d 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -3,10 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <utility> 5#include <utility>
6
7#include "common/assert.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
7#include "core/core.h" 9#include "core/core.h"
8#include "core/hle/kernel/errors.h" 10#include "core/hle/kernel/errors.h"
9#include "core/hle/kernel/memory.h"
10#include "core/hle/kernel/shared_memory.h" 11#include "core/hle/kernel/shared_memory.h"
11#include "core/memory.h" 12#include "core/memory.h"
12 13
@@ -28,35 +29,17 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
28 shared_memory->other_permissions = other_permissions; 29 shared_memory->other_permissions = other_permissions;
29 30
30 if (address == 0) { 31 if (address == 0) {
31 // We need to allocate a block from the Linear Heap ourselves. 32 shared_memory->backing_block = std::make_shared<std::vector<u8>>(size);
32 // We'll manually allocate some memory from the linear heap in the specified region. 33 shared_memory->backing_block_offset = 0;
33 MemoryRegionInfo* memory_region = GetMemoryRegion(region);
34 auto& linheap_memory = memory_region->linear_heap_memory;
35
36 ASSERT_MSG(linheap_memory->size() + size <= memory_region->size,
37 "Not enough space in region to allocate shared memory!");
38
39 shared_memory->backing_block = linheap_memory;
40 shared_memory->backing_block_offset = linheap_memory->size();
41 // Allocate some memory from the end of the linear heap for this region.
42 linheap_memory->insert(linheap_memory->end(), size, 0);
43 memory_region->used += size;
44
45 shared_memory->linear_heap_phys_address =
46 Memory::FCRAM_PADDR + memory_region->base +
47 static_cast<PAddr>(shared_memory->backing_block_offset);
48
49 // Increase the amount of used linear heap memory for the owner process.
50 if (shared_memory->owner_process != nullptr) {
51 shared_memory->owner_process->linear_heap_used += size;
52 }
53 34
54 // Refresh the address mappings for the current process. 35 // Refresh the address mappings for the current process.
55 if (Core::CurrentProcess() != nullptr) { 36 if (Core::CurrentProcess() != nullptr) {
56 Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 37 Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(
38 shared_memory->backing_block.get());
57 } 39 }
58 } else { 40 } else {
59 auto& vm_manager = shared_memory->owner_process->vm_manager; 41 auto& vm_manager = shared_memory->owner_process->vm_manager;
42
60 // The memory is already available and mapped in the owner process. 43 // The memory is already available and mapped in the owner process.
61 auto vma = vm_manager.FindVMA(address); 44 auto vma = vm_manager.FindVMA(address);
62 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); 45 ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
@@ -72,6 +55,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u
72 } 55 }
73 56
74 shared_memory->base_address = address; 57 shared_memory->base_address = address;
58
75 return shared_memory; 59 return shared_memory;
76} 60}
77 61
@@ -122,11 +106,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
122 106
123 VAddr target_address = address; 107 VAddr target_address = address;
124 108
125 if (base_address == 0 && target_address == 0) {
126 // Calculate the address at which to map the memory block.
127 target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address).value();
128 }
129
130 // Map the memory block into the target process 109 // Map the memory block into the target process
131 auto result = target_process->vm_manager.MapMemoryBlock( 110 auto result = target_process->vm_manager.MapMemoryBlock(
132 target_address, backing_block, backing_block_offset, size, MemoryState::Shared); 111 target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 86f818e90..c50fee615 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -4,9 +4,12 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <string> 8#include <string>
9#include <vector>
10
8#include "common/common_types.h" 11#include "common/common_types.h"
9#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/object.h"
10#include "core/hle/kernel/process.h" 13#include "core/hle/kernel/process.h"
11#include "core/hle/result.h" 14#include "core/hle/result.h"
12 15
@@ -108,9 +111,6 @@ public:
108 SharedPtr<Process> owner_process; 111 SharedPtr<Process> owner_process;
109 /// Address of shared memory block in the owner process if specified. 112 /// Address of shared memory block in the owner process if specified.
110 VAddr base_address; 113 VAddr base_address;
111 /// Physical address of the shared memory block in the linear heap if no address was specified
112 /// during creation.
113 PAddr linear_heap_phys_address;
114 /// Backing memory for this shared memory block. 114 /// Backing memory for this shared memory block.
115 std::shared_ptr<std::vector<u8>> backing_block; 115 std::shared_ptr<std::vector<u8>> backing_block;
116 /// Offset into the backing block for this shared memory. 116 /// Offset into the backing block for this shared memory.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 0b439401a..5db2db687 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -5,7 +5,10 @@
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes> 6#include <cinttypes>
7#include <iterator> 7#include <iterator>
8#include <mutex>
9#include <vector>
8 10
11#include "common/assert.h"
9#include "common/logging/log.h" 12#include "common/logging/log.h"
10#include "common/microprofile.h" 13#include "common/microprofile.h"
11#include "common/string_util.h" 14#include "common/string_util.h"
@@ -17,7 +20,6 @@
17#include "core/hle/kernel/event.h" 20#include "core/hle/kernel/event.h"
18#include "core/hle/kernel/handle_table.h" 21#include "core/hle/kernel/handle_table.h"
19#include "core/hle/kernel/mutex.h" 22#include "core/hle/kernel/mutex.h"
20#include "core/hle/kernel/object_address_table.h"
21#include "core/hle/kernel/process.h" 23#include "core/hle/kernel/process.h"
22#include "core/hle/kernel/resource_limit.h" 24#include "core/hle/kernel/resource_limit.h"
23#include "core/hle/kernel/shared_memory.h" 25#include "core/hle/kernel/shared_memory.h"
@@ -265,7 +267,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
265 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, 267 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
266 info_sub_id, handle); 268 info_sub_id, handle);
267 269
268 auto& vm_manager = Core::CurrentProcess()->vm_manager; 270 const auto& vm_manager = Core::CurrentProcess()->vm_manager;
269 271
270 switch (static_cast<GetInfoType>(info_id)) { 272 switch (static_cast<GetInfoType>(info_id)) {
271 case GetInfoType::AllowedCpuIdBitmask: 273 case GetInfoType::AllowedCpuIdBitmask:
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 94735c86e..b9022feae 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -4,8 +4,11 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes> 6#include <cinttypes>
7#include <list>
8#include <vector> 7#include <vector>
8
9#include <boost/optional.hpp>
10#include <boost/range/algorithm_ext/erase.hpp>
11
9#include "common/assert.h" 12#include "common/assert.h"
10#include "common/common_types.h" 13#include "common/common_types.h"
11#include "common/logging/log.h" 14#include "common/logging/log.h"
@@ -17,9 +20,7 @@
17#include "core/core_timing_util.h" 20#include "core/core_timing_util.h"
18#include "core/hle/kernel/errors.h" 21#include "core/hle/kernel/errors.h"
19#include "core/hle/kernel/handle_table.h" 22#include "core/hle/kernel/handle_table.h"
20#include "core/hle/kernel/kernel.h" 23#include "core/hle/kernel/object.h"
21#include "core/hle/kernel/memory.h"
22#include "core/hle/kernel/mutex.h"
23#include "core/hle/kernel/process.h" 24#include "core/hle/kernel/process.h"
24#include "core/hle/kernel/thread.h" 25#include "core/hle/kernel/thread.h"
25#include "core/hle/result.h" 26#include "core/hle/result.h"
@@ -79,8 +80,8 @@ void Thread::Stop() {
79 wait_objects.clear(); 80 wait_objects.clear();
80 81
81 // Mark the TLS slot in the thread's page as free. 82 // Mark the TLS slot in the thread's page as free.
82 u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; 83 const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
83 u64 tls_slot = 84 const u64 tls_slot =
84 ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; 85 ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
85 Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); 86 Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
86} 87}
@@ -250,13 +251,14 @@ void Thread::ResumeFromWait() {
250 * slot: The index of the first free slot in the indicated page. 251 * slot: The index of the first free slot in the indicated page.
251 * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). 252 * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
252 */ 253 */
253std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) { 254static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
255 const std::vector<std::bitset<8>>& tls_slots) {
254 // Iterate over all the allocated pages, and try to find one where not all slots are used. 256 // Iterate over all the allocated pages, and try to find one where not all slots are used.
255 for (unsigned page = 0; page < tls_slots.size(); ++page) { 257 for (std::size_t page = 0; page < tls_slots.size(); ++page) {
256 const auto& page_tls_slots = tls_slots[page]; 258 const auto& page_tls_slots = tls_slots[page];
257 if (!page_tls_slots.all()) { 259 if (!page_tls_slots.all()) {
258 // We found a page with at least one free slot, find which slot it is 260 // We found a page with at least one free slot, find which slot it is
259 for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) { 261 for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
260 if (!page_tls_slots.test(slot)) { 262 if (!page_tls_slots.test(slot)) {
261 return std::make_tuple(page, slot, false); 263 return std::make_tuple(page, slot, false);
262 } 264 }
@@ -331,42 +333,22 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
331 333
332 // Find the next available TLS index, and mark it as used 334 // Find the next available TLS index, and mark it as used
333 auto& tls_slots = owner_process->tls_slots; 335 auto& tls_slots = owner_process->tls_slots;
334 bool needs_allocation = true;
335 u32 available_page; // Which allocated page has free space
336 u32 available_slot; // Which slot within the page is free
337
338 std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots);
339 336
337 auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
340 if (needs_allocation) { 338 if (needs_allocation) {
341 // There are no already-allocated pages with free slots, lets allocate a new one.
342 // TLS pages are allocated from the BASE region in the linear heap.
343 MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE);
344 auto& linheap_memory = memory_region->linear_heap_memory;
345
346 if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
347 LOG_ERROR(Kernel_SVC,
348 "Not enough space in region to allocate a new TLS page for thread");
349 return ERR_OUT_OF_MEMORY;
350 }
351
352 size_t offset = linheap_memory->size();
353
354 // Allocate some memory from the end of the linear heap for this region.
355 linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0);
356 memory_region->used += Memory::PAGE_SIZE;
357 owner_process->linear_heap_used += Memory::PAGE_SIZE;
358
359 tls_slots.emplace_back(0); // The page is completely available at the start 339 tls_slots.emplace_back(0); // The page is completely available at the start
360 available_page = static_cast<u32>(tls_slots.size() - 1); 340 available_page = tls_slots.size() - 1;
361 available_slot = 0; // Use the first slot in the new page 341 available_slot = 0; // Use the first slot in the new page
362 342
343 // Allocate some memory from the end of the linear heap for this region.
344 const size_t offset = thread->tls_memory->size();
345 thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
346
363 auto& vm_manager = owner_process->vm_manager; 347 auto& vm_manager = owner_process->vm_manager;
364 vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); 348 vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
365 349
366 // Map the page to the current process' address space.
367 // TODO(Subv): Find the correct MemoryState for this region.
368 vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, 350 vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
369 linheap_memory, offset, Memory::PAGE_SIZE, 351 thread->tls_memory, 0, Memory::PAGE_SIZE,
370 MemoryState::ThreadLocal); 352 MemoryState::ThreadLocal);
371 } 353 }
372 354
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 6218960d2..adc804248 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -4,15 +4,14 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
7#include <memory> 8#include <memory>
8#include <string> 9#include <string>
9#include <unordered_map>
10#include <vector> 10#include <vector>
11#include <boost/container/flat_map.hpp> 11
12#include <boost/container/flat_set.hpp>
13#include "common/common_types.h" 12#include "common/common_types.h"
14#include "core/arm/arm_interface.h" 13#include "core/arm/arm_interface.h"
15#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/object.h"
16#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
17#include "core/hle/result.h" 16#include "core/hle/result.h"
18 17
@@ -266,6 +265,8 @@ public:
266private: 265private:
267 Thread(); 266 Thread();
268 ~Thread() override; 267 ~Thread() override;
268
269 std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
269}; 270};
270 271
271/** 272/**
@@ -289,12 +290,6 @@ Thread* GetCurrentThread();
289void WaitCurrentThread_Sleep(); 290void WaitCurrentThread_Sleep();
290 291
291/** 292/**
292 * Waits the current thread from an ArbitrateAddress call
293 * @param wait_address Arbitration address used to resume from wait
294 */
295void WaitCurrentThread_ArbitrateAddress(VAddr wait_address);
296
297/**
298 * Stops the current thread and removes it from the thread_list 293 * Stops the current thread and removes it from the thread_list
299 */ 294 */
300void ExitCurrentThread(); 295void ExitCurrentThread();
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 904a3d0a5..282360745 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -8,7 +8,7 @@
8#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/core_timing_util.h" 9#include "core/core_timing_util.h"
10#include "core/hle/kernel/handle_table.h" 10#include "core/hle/kernel/handle_table.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/object.h"
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
13#include "core/hle/kernel/timer.h" 13#include "core/hle/kernel/timer.h"
14 14
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
index c63f0ed90..4dddc67e0 100644
--- a/src/core/hle/kernel/timer.h
+++ b/src/core/hle/kernel/timer.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h" 8#include "core/hle/kernel/object.h"
9#include "core/hle/kernel/wait_object.h" 9#include "core/hle/kernel/wait_object.h"
10 10
11namespace Kernel { 11namespace Kernel {
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 9d26fd781..479cacb62 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.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 <iterator> 6#include <iterator>
6#include <utility> 7#include <utility>
7#include "common/assert.h" 8#include "common/assert.h"
@@ -175,9 +176,9 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
175 176
176ResultCode VMManager::UnmapRange(VAddr target, u64 size) { 177ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
177 CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); 178 CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
178 VAddr target_end = target + size; 179 const VAddr target_end = target + size;
179 180
180 VMAIter end = vma_map.end(); 181 const VMAIter end = vma_map.end();
181 // The comparison against the end of the range must be done using addresses since VMAs can be 182 // The comparison against the end of the range must be done using addresses since VMAs can be
182 // merged during this process, causing invalidation of the iterators. 183 // merged during this process, causing invalidation of the iterators.
183 while (vma != end && vma->second.base < target_end) { 184 while (vma != end && vma->second.base < target_end) {
@@ -207,9 +208,9 @@ VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission ne
207 208
208ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) { 209ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) {
209 CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); 210 CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size));
210 VAddr target_end = target + size; 211 const VAddr target_end = target + size;
211 212
212 VMAIter end = vma_map.end(); 213 const VMAIter end = vma_map.end();
213 // The comparison against the end of the range must be done using addresses since VMAs can be 214 // The comparison against the end of the range must be done using addresses since VMAs can be
214 // merged during this process, causing invalidation of the iterators. 215 // merged during this process, causing invalidation of the iterators.
215 while (vma != end && vma->second.base < target_end) { 216 while (vma != end && vma->second.base < target_end) {
@@ -258,14 +259,14 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) {
258 return ERR_INVALID_ADDRESS; 259 return ERR_INVALID_ADDRESS;
259 } 260 }
260 261
261 VirtualMemoryArea& vma = vma_handle->second; 262 const VirtualMemoryArea& vma = vma_handle->second;
262 if (vma.type != VMAType::Free) { 263 if (vma.type != VMAType::Free) {
263 // Region is already allocated 264 // Region is already allocated
264 return ERR_INVALID_ADDRESS_STATE; 265 return ERR_INVALID_ADDRESS_STATE;
265 } 266 }
266 267
267 u64 start_in_vma = base - vma.base; 268 const VAddr start_in_vma = base - vma.base;
268 u64 end_in_vma = start_in_vma + size; 269 const VAddr end_in_vma = start_in_vma + size;
269 270
270 if (end_in_vma > vma.size) { 271 if (end_in_vma > vma.size) {
271 // Requested allocation doesn't fit inside VMA 272 // Requested allocation doesn't fit inside VMA
@@ -288,17 +289,16 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) {
288 ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size); 289 ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size);
289 ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", target); 290 ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", target);
290 291
291 VAddr target_end = target + size; 292 const VAddr target_end = target + size;
292 ASSERT(target_end >= target); 293 ASSERT(target_end >= target);
293 ASSERT(target_end <= MAX_ADDRESS); 294 ASSERT(target_end <= MAX_ADDRESS);
294 ASSERT(size > 0); 295 ASSERT(size > 0);
295 296
296 VMAIter begin_vma = StripIterConstness(FindVMA(target)); 297 VMAIter begin_vma = StripIterConstness(FindVMA(target));
297 VMAIter i_end = vma_map.lower_bound(target_end); 298 const VMAIter i_end = vma_map.lower_bound(target_end);
298 for (auto i = begin_vma; i != i_end; ++i) { 299 if (std::any_of(begin_vma, i_end,
299 if (i->second.type == VMAType::Free) { 300 [](const auto& entry) { return entry.second.type == VMAType::Free; })) {
300 return ERR_INVALID_ADDRESS_STATE; 301 return ERR_INVALID_ADDRESS_STATE;
301 }
302 } 302 }
303 303
304 if (target != begin_vma->second.base) { 304 if (target != begin_vma->second.base) {
@@ -346,7 +346,7 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
346} 346}
347 347
348VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { 348VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
349 VMAIter next_vma = std::next(iter); 349 const VMAIter next_vma = std::next(iter);
350 if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { 350 if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
351 iter->second.size += next_vma->second.size; 351 iter->second.size += next_vma->second.size;
352 vma_map.erase(next_vma); 352 vma_map.erase(next_vma);
@@ -382,22 +382,22 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
382 } 382 }
383} 383}
384 384
385u64 VMManager::GetTotalMemoryUsage() { 385u64 VMManager::GetTotalMemoryUsage() const {
386 LOG_WARNING(Kernel, "(STUBBED) called"); 386 LOG_WARNING(Kernel, "(STUBBED) called");
387 return 0xF8000000; 387 return 0xF8000000;
388} 388}
389 389
390u64 VMManager::GetTotalHeapUsage() { 390u64 VMManager::GetTotalHeapUsage() const {
391 LOG_WARNING(Kernel, "(STUBBED) called"); 391 LOG_WARNING(Kernel, "(STUBBED) called");
392 return 0x0; 392 return 0x0;
393} 393}
394 394
395VAddr VMManager::GetAddressSpaceBaseAddr() { 395VAddr VMManager::GetAddressSpaceBaseAddr() const {
396 LOG_WARNING(Kernel, "(STUBBED) called"); 396 LOG_WARNING(Kernel, "(STUBBED) called");
397 return 0x8000000; 397 return 0x8000000;
398} 398}
399 399
400u64 VMManager::GetAddressSpaceSize() { 400u64 VMManager::GetAddressSpaceSize() const {
401 LOG_WARNING(Kernel, "(STUBBED) called"); 401 LOG_WARNING(Kernel, "(STUBBED) called");
402 return MAX_ADDRESS; 402 return MAX_ADDRESS;
403} 403}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 38e4ebcd3..98bd04bea 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -190,16 +190,16 @@ public:
190 void LogLayout() const; 190 void LogLayout() const;
191 191
192 /// Gets the total memory usage, used by svcGetInfo 192 /// Gets the total memory usage, used by svcGetInfo
193 u64 GetTotalMemoryUsage(); 193 u64 GetTotalMemoryUsage() const;
194 194
195 /// Gets the total heap usage, used by svcGetInfo 195 /// Gets the total heap usage, used by svcGetInfo
196 u64 GetTotalHeapUsage(); 196 u64 GetTotalHeapUsage() const;
197 197
198 /// Gets the total address space base address, used by svcGetInfo 198 /// Gets the total address space base address, used by svcGetInfo
199 VAddr GetAddressSpaceBaseAddr(); 199 VAddr GetAddressSpaceBaseAddr() const;
200 200
201 /// Gets the total address space address size, used by svcGetInfo 201 /// Gets the total address space address size, used by svcGetInfo
202 u64 GetAddressSpaceSize(); 202 u64 GetAddressSpaceSize() const;
203 203
204 /// Each VMManager has its own page table, which is set as the main one when the owning process 204 /// Each VMManager has its own page table, which is set as the main one when the owning process
205 /// is scheduled. 205 /// is scheduled.
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index 23af346d0..7681cdee7 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -5,11 +5,8 @@
5#include <algorithm> 5#include <algorithm>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/kernel/errors.h" 8#include "core/hle/kernel/object.h"
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/memory.h"
11#include "core/hle/kernel/process.h" 9#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/resource_limit.h"
13#include "core/hle/kernel/thread.h" 10#include "core/hle/kernel/thread.h"
14#include "core/hle/kernel/timer.h" 11#include "core/hle/kernel/timer.h"
15 12
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
index 78bfd8c6c..b5fbc647b 100644
--- a/src/core/hle/kernel/wait_object.h
+++ b/src/core/hle/kernel/wait_object.h
@@ -7,7 +7,7 @@
7#include <vector> 7#include <vector>
8#include <boost/smart_ptr/intrusive_ptr.hpp> 8#include <boost/smart_ptr/intrusive_ptr.hpp>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/object.h"
11 11
12namespace Kernel { 12namespace Kernel {
13 13
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 0b158e015..6d15b46ed 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -10,6 +10,7 @@
10#include "core/hle/service/acc/acc_su.h" 10#include "core/hle/service/acc/acc_su.h"
11#include "core/hle/service/acc/acc_u0.h" 11#include "core/hle/service/acc/acc_u0.h"
12#include "core/hle/service/acc/acc_u1.h" 12#include "core/hle/service/acc/acc_u1.h"
13#include "core/settings.h"
13 14
14namespace Service::Account { 15namespace Service::Account {
15 16
@@ -31,13 +32,14 @@ struct ProfileBase {
31}; 32};
32static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size"); 33static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size");
33 34
35// TODO(ogniK): Generate a real user id based on username, md5(username) maybe?
34static constexpr u128 DEFAULT_USER_ID{1ull, 0ull}; 36static constexpr u128 DEFAULT_USER_ID{1ull, 0ull};
35 37
36class IProfile final : public ServiceFramework<IProfile> { 38class IProfile final : public ServiceFramework<IProfile> {
37public: 39public:
38 explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) { 40 explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) {
39 static const FunctionInfo functions[] = { 41 static const FunctionInfo functions[] = {
40 {0, nullptr, "Get"}, 42 {0, &IProfile::Get, "Get"},
41 {1, &IProfile::GetBase, "GetBase"}, 43 {1, &IProfile::GetBase, "GetBase"},
42 {10, nullptr, "GetImageSize"}, 44 {10, nullptr, "GetImageSize"},
43 {11, nullptr, "LoadImage"}, 45 {11, nullptr, "LoadImage"},
@@ -46,14 +48,36 @@ public:
46 } 48 }
47 49
48private: 50private:
51 void Get(Kernel::HLERequestContext& ctx) {
52 LOG_WARNING(Service_ACC, "(STUBBED) called");
53 ProfileBase profile_base{};
54 profile_base.user_id = user_id;
55 if (Settings::values.username.size() > profile_base.username.size()) {
56 std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
57 profile_base.username.begin());
58 } else {
59 std::copy(Settings::values.username.begin(), Settings::values.username.end(),
60 profile_base.username.begin());
61 }
62
63 IPC::ResponseBuilder rb{ctx, 16};
64 rb.Push(RESULT_SUCCESS);
65 rb.PushRaw(profile_base);
66 }
67
49 void GetBase(Kernel::HLERequestContext& ctx) { 68 void GetBase(Kernel::HLERequestContext& ctx) {
50 LOG_WARNING(Service_ACC, "(STUBBED) called"); 69 LOG_WARNING(Service_ACC, "(STUBBED) called");
51 70
52 // TODO(Subv): Retrieve this information from somewhere. 71 // TODO(Subv): Retrieve this information from somewhere.
53 ProfileBase profile_base{}; 72 ProfileBase profile_base{};
54 profile_base.user_id = user_id; 73 profile_base.user_id = user_id;
55 profile_base.username = {'y', 'u', 'z', 'u'}; 74 if (Settings::values.username.size() > profile_base.username.size()) {
56 75 std::copy_n(Settings::values.username.begin(), profile_base.username.size(),
76 profile_base.username.begin());
77 } else {
78 std::copy(Settings::values.username.begin(), Settings::values.username.end(),
79 profile_base.username.begin());
80 }
57 IPC::ResponseBuilder rb{ctx, 16}; 81 IPC::ResponseBuilder rb{ctx, 16};
58 rb.Push(RESULT_SUCCESS); 82 rb.Push(RESULT_SUCCESS);
59 rb.PushRaw(profile_base); 83 rb.PushRaw(profile_base);
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 97ef07bf9..9404d6b8c 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -11,6 +11,9 @@
11#include "core/hle/service/am/am.h" 11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applet_ae.h" 12#include "core/hle/service/am/applet_ae.h"
13#include "core/hle/service/am/applet_oe.h" 13#include "core/hle/service/am/applet_oe.h"
14#include "core/hle/service/am/idle.h"
15#include "core/hle/service/am/omm.h"
16#include "core/hle/service/am/spsm.h"
14#include "core/hle/service/apm/apm.h" 17#include "core/hle/service/apm/apm.h"
15#include "core/hle/service/filesystem/filesystem.h" 18#include "core/hle/service/filesystem/filesystem.h"
16#include "core/hle/service/nvflinger/nvflinger.h" 19#include "core/hle/service/nvflinger/nvflinger.h"
@@ -649,7 +652,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
649 // TODO(bunnei): This should be configurable 652 // TODO(bunnei): This should be configurable
650 IPC::ResponseBuilder rb{ctx, 4}; 653 IPC::ResponseBuilder rb{ctx, 4};
651 rb.Push(RESULT_SUCCESS); 654 rb.Push(RESULT_SUCCESS);
652 rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US)); 655 rb.Push(
656 static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index)));
653 LOG_DEBUG(Service_AM, "called"); 657 LOG_DEBUG(Service_AM, "called");
654} 658}
655 659
@@ -689,6 +693,9 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
689 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 693 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
690 std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); 694 std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
691 std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); 695 std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
696 std::make_shared<IdleSys>()->InstallAsService(service_manager);
697 std::make_shared<OMM>()->InstallAsService(service_manager);
698 std::make_shared<SPSM>()->InstallAsService(service_manager);
692} 699}
693 700
694IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { 701IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp
new file mode 100644
index 000000000..af46e9494
--- /dev/null
+++ b/src/core/hle/service/am/idle.cpp
@@ -0,0 +1,24 @@
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/idle.h"
6
7namespace Service::AM {
8
9IdleSys::IdleSys() : ServiceFramework{"idle:sys"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetAutoPowerDownEvent"},
13 {1, nullptr, "Unknown1"},
14 {2, nullptr, "Unknown2"},
15 {3, nullptr, "Unknown3"},
16 {4, nullptr, "Unknown4"},
17 {5, nullptr, "Unknown5"},
18 };
19 // clang-format on
20
21 RegisterHandlers(functions);
22}
23
24} // namespace Service::AM
diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h
new file mode 100644
index 000000000..1eb68d2c9
--- /dev/null
+++ b/src/core/hle/service/am/idle.h
@@ -0,0 +1,16 @@
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 IdleSys final : public ServiceFramework<IdleSys> {
12public:
13 explicit IdleSys();
14};
15
16} // namespace Service::AM
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
new file mode 100644
index 000000000..447fe8669
--- /dev/null
+++ b/src/core/hle/service/am/omm.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 "core/hle/service/am/omm.h"
6
7namespace Service::AM {
8
9OMM::OMM() : ServiceFramework{"omm"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetOperationMode"},
13 {1, nullptr, "GetOperationModeChangeEvent"},
14 {2, nullptr, "EnableAudioVisual"},
15 {3, nullptr, "DisableAudioVisual"},
16 {4, nullptr, "EnterSleepAndWait"},
17 {5, nullptr, "GetCradleStatus"},
18 {6, nullptr, "FadeInDisplay"},
19 {7, nullptr, "FadeOutDisplay"},
20 {8, nullptr, "Unknown1"},
21 {9, nullptr, "Unknown2"},
22 {10, nullptr, "Unknown3"},
23 {11, nullptr, "Unknown4"},
24 {12, nullptr, "Unknown5"},
25 {13, nullptr, "Unknown6"},
26 {14, nullptr, "Unknown7"},
27 {15, nullptr, "Unknown8"},
28 {16, nullptr, "Unknown9"},
29 {17, nullptr, "Unknown10"},
30 {18, nullptr, "Unknown11"},
31 {19, nullptr, "Unknown12"},
32 {20, nullptr, "Unknown13"},
33 {21, nullptr, "Unknown14"},
34 {22, nullptr, "Unknown15"},
35 {23, nullptr, "Unknown16"},
36 };
37 // clang-format on
38
39 RegisterHandlers(functions);
40}
41
42} // namespace Service::AM
diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h
new file mode 100644
index 000000000..49e5d331c
--- /dev/null
+++ b/src/core/hle/service/am/omm.h
@@ -0,0 +1,16 @@
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 OMM final : public ServiceFramework<OMM> {
12public:
13 explicit OMM();
14};
15
16} // namespace Service::AM
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp
new file mode 100644
index 000000000..a05d433d0
--- /dev/null
+++ b/src/core/hle/service/am/spsm.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/am/spsm.h"
6
7namespace Service::AM {
8
9SPSM::SPSM() : ServiceFramework{"spsm"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetState"},
13 {1, nullptr, "SleepSystemAndWaitAwake"},
14 {2, nullptr, "Unknown1"},
15 {3, nullptr, "Unknown2"},
16 {4, nullptr, "GetNotificationMessageEventHandle"},
17 {5, nullptr, "Unknown3"},
18 {6, nullptr, "Unknown4"},
19 {7, nullptr, "Unknown5"},
20 {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"},
21 {9, nullptr, "ChangeHomeButtonLongPressingTime"},
22 {10, nullptr, "Unknown6"},
23 {11, nullptr, "Unknown7"},
24 };
25 // clang-format on
26
27 RegisterHandlers(functions);
28}
29
30} // namespace Service::AM
diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h
new file mode 100644
index 000000000..57dde62e1
--- /dev/null
+++ b/src/core/hle/service/am/spsm.h
@@ -0,0 +1,16 @@
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 SPSM final : public ServiceFramework<SPSM> {
12public:
13 explicit SPSM();
14};
15
16} // namespace Service::AM
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 7a185c6c8..4109cb7f7 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.cpp
@@ -13,6 +13,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
13 auto module_ = std::make_shared<Module>(); 13 auto module_ = std::make_shared<Module>();
14 std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); 14 std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager);
15 std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); 15 std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager);
16 std::make_shared<APM_Sys>()->InstallAsService(service_manager);
16} 17}
17 18
18} // namespace Service::APM 19} // namespace Service::APM
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index ce943d829..4cd8132f5 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -74,6 +74,31 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) {
74 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 74 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
75 rb.Push(RESULT_SUCCESS); 75 rb.Push(RESULT_SUCCESS);
76 rb.PushIpcInterface<ISession>(); 76 rb.PushIpcInterface<ISession>();
77
78 LOG_DEBUG(Service_APM, "called");
79}
80
81APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
82 // clang-format off
83 static const FunctionInfo functions[] = {
84 {0, nullptr, "RequestPerformanceMode"},
85 {1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"},
86 {2, nullptr, "GetThrottlingState"},
87 {3, nullptr, "GetLastThrottlingState"},
88 {4, nullptr, "ClearLastThrottlingState"},
89 {5, nullptr, "LoadAndApplySettings"},
90 };
91 // clang-format on
92
93 RegisterHandlers(functions);
94}
95
96void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) {
97 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
98 rb.Push(RESULT_SUCCESS);
99 rb.PushIpcInterface<ISession>();
100
101 LOG_DEBUG(Service_APM, "called");
77} 102}
78 103
79} // namespace Service::APM 104} // namespace Service::APM
diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h
index fa68c7d93..d14264ad7 100644
--- a/src/core/hle/service/apm/interface.h
+++ b/src/core/hle/service/apm/interface.h
@@ -19,4 +19,12 @@ private:
19 std::shared_ptr<Module> apm; 19 std::shared_ptr<Module> apm;
20}; 20};
21 21
22class APM_Sys final : public ServiceFramework<APM_Sys> {
23public:
24 explicit APM_Sys();
25
26private:
27 void GetPerformanceEvent(Kernel::HLERequestContext& ctx);
28};
29
22} // namespace Service::APM 30} // namespace Service::APM
diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp
new file mode 100644
index 000000000..358ef2576
--- /dev/null
+++ b/src/core/hle/service/arp/arp.cpp
@@ -0,0 +1,75 @@
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/kernel/hle_ipc.h"
10#include "core/hle/service/arp/arp.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::ARP {
15
16class ARP_R final : public ServiceFramework<ARP_R> {
17public:
18 explicit ARP_R() : ServiceFramework{"arp:r"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "GetApplicationLaunchProperty"},
22 {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
23 {2, nullptr, "GetApplicationControlProperty"},
24 {3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
25 };
26 // clang-format on
27
28 RegisterHandlers(functions);
29 }
30};
31
32class IRegistrar final : public ServiceFramework<IRegistrar> {
33public:
34 explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
35 // clang-format off
36 static const FunctionInfo functions[] = {
37 {0, nullptr, "Issue"},
38 {1, nullptr, "SetApplicationLaunchProperty"},
39 {2, nullptr, "SetApplicationControlProperty"},
40 };
41 // clang-format on
42
43 RegisterHandlers(functions);
44 }
45};
46
47class ARP_W final : public ServiceFramework<ARP_W> {
48public:
49 explicit ARP_W() : ServiceFramework{"arp:w"} {
50 // clang-format off
51 static const FunctionInfo functions[] = {
52 {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
53 {1, nullptr, "DeleteProperties"},
54 };
55 // clang-format on
56
57 RegisterHandlers(functions);
58 }
59
60private:
61 void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
62 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
63 rb.Push(RESULT_SUCCESS);
64 rb.PushIpcInterface<IRegistrar>();
65
66 LOG_DEBUG(Service_ARP, "called");
67 }
68};
69
70void InstallInterfaces(SM::ServiceManager& sm) {
71 std::make_shared<ARP_R>()->InstallAsService(sm);
72 std::make_shared<ARP_W>()->InstallAsService(sm);
73}
74
75} // namespace Service::ARP
diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h
new file mode 100644
index 000000000..9d100187c
--- /dev/null
+++ b/src/core/hle/service/arp/arp.h
@@ -0,0 +1,16 @@
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::ARP {
12
13/// Registers all ARP services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm);
15
16} // namespace Service::ARP
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
new file mode 100644
index 000000000..37c3fdcac
--- /dev/null
+++ b/src/core/hle/service/audio/audctl.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 "core/hle/service/audio/audctl.h"
6
7namespace Service::Audio {
8
9AudCtl::AudCtl() : ServiceFramework{"audctl"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "GetTargetVolume"},
13 {1, nullptr, "SetTargetVolume"},
14 {2, nullptr, "GetTargetVolumeMin"},
15 {3, nullptr, "GetTargetVolumeMax"},
16 {4, nullptr, "IsTargetMute"},
17 {5, nullptr, "SetTargetMute"},
18 {6, nullptr, "IsTargetConnected"},
19 {7, nullptr, "SetDefaultTarget"},
20 {8, nullptr, "GetDefaultTarget"},
21 {9, nullptr, "GetAudioOutputMode"},
22 {10, nullptr, "SetAudioOutputMode"},
23 {11, nullptr, "SetForceMutePolicy"},
24 {12, nullptr, "GetForceMutePolicy"},
25 {13, nullptr, "GetOutputModeSetting"},
26 {14, nullptr, "SetOutputModeSetting"},
27 {15, nullptr, "SetOutputTarget"},
28 {16, nullptr, "SetInputTargetForceEnabled"},
29 {17, nullptr, "SetHeadphoneOutputLevelMode"},
30 {18, nullptr, "GetHeadphoneOutputLevelMode"},
31 {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"},
32 {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"},
33 {21, nullptr, "GetAudioOutputTargetForPlayReport"},
34 {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"},
35 {23, nullptr, "SetSystemOutputMasterVolume"},
36 {24, nullptr, "GetSystemOutputMasterVolume"},
37 {25, nullptr, "GetAudioVolumeDataForPlayReport"},
38 {26, nullptr, "UpdateHeadphoneSettings"},
39 };
40 // clang-format on
41
42 RegisterHandlers(functions);
43}
44
45} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h
new file mode 100644
index 000000000..ed837bdf2
--- /dev/null
+++ b/src/core/hle/service/audio/audctl.h
@@ -0,0 +1,16 @@
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::Audio {
10
11class AudCtl final : public ServiceFramework<AudCtl> {
12public:
13 explicit AudCtl();
14};
15
16} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp
new file mode 100644
index 000000000..b08c21a20
--- /dev/null
+++ b/src/core/hle/service/audio/auddbg.cpp
@@ -0,0 +1,20 @@
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/audio/auddbg.h"
6
7namespace Service::Audio {
8
9AudDbg::AudDbg(const char* name) : ServiceFramework{name} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "RequestSuspendForDebug"},
13 {1, nullptr, "RequestResumeForDebug"},
14 };
15 // clang-format on
16
17 RegisterHandlers(functions);
18}
19
20} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h
new file mode 100644
index 000000000..a2f540b75
--- /dev/null
+++ b/src/core/hle/service/audio/auddbg.h
@@ -0,0 +1,16 @@
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::Audio {
10
11class AudDbg final : public ServiceFramework<AudDbg> {
12public:
13 explicit AudDbg(const char* name);
14};
15
16} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp
new file mode 100644
index 000000000..a70d5bca4
--- /dev/null
+++ b/src/core/hle/service/audio/audin_a.cpp
@@ -0,0 +1,22 @@
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/audio/audin_a.h"
6
7namespace Service::Audio {
8
9AudInA::AudInA() : ServiceFramework{"audin:a"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "RequestSuspendAudioIns"},
13 {1, nullptr, "RequestResumeAudioIns"},
14 {2, nullptr, "GetAudioInsProcessMasterVolume"},
15 {3, nullptr, "SetAudioInsProcessMasterVolume"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h
new file mode 100644
index 000000000..e4c75510f
--- /dev/null
+++ b/src/core/hle/service/audio/audin_a.h
@@ -0,0 +1,16 @@
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::Audio {
10
11class AudInA final : public ServiceFramework<AudInA> {
12public:
13 explicit AudInA();
14};
15
16} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index d231e91e1..6b5e15633 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -2,10 +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 "core/hle/service/audio/audctl.h"
6#include "core/hle/service/audio/auddbg.h"
7#include "core/hle/service/audio/audin_a.h"
5#include "core/hle/service/audio/audin_u.h" 8#include "core/hle/service/audio/audin_u.h"
6#include "core/hle/service/audio/audio.h" 9#include "core/hle/service/audio/audio.h"
10#include "core/hle/service/audio/audout_a.h"
7#include "core/hle/service/audio/audout_u.h" 11#include "core/hle/service/audio/audout_u.h"
12#include "core/hle/service/audio/audrec_a.h"
8#include "core/hle/service/audio/audrec_u.h" 13#include "core/hle/service/audio/audrec_u.h"
14#include "core/hle/service/audio/audren_a.h"
9#include "core/hle/service/audio/audren_u.h" 15#include "core/hle/service/audio/audren_u.h"
10#include "core/hle/service/audio/codecctl.h" 16#include "core/hle/service/audio/codecctl.h"
11#include "core/hle/service/audio/hwopus.h" 17#include "core/hle/service/audio/hwopus.h"
@@ -13,12 +19,22 @@
13namespace Service::Audio { 19namespace Service::Audio {
14 20
15void InstallInterfaces(SM::ServiceManager& service_manager) { 21void InstallInterfaces(SM::ServiceManager& service_manager) {
22 std::make_shared<AudCtl>()->InstallAsService(service_manager);
23 std::make_shared<AudOutA>()->InstallAsService(service_manager);
16 std::make_shared<AudOutU>()->InstallAsService(service_manager); 24 std::make_shared<AudOutU>()->InstallAsService(service_manager);
25 std::make_shared<AudInA>()->InstallAsService(service_manager);
17 std::make_shared<AudInU>()->InstallAsService(service_manager); 26 std::make_shared<AudInU>()->InstallAsService(service_manager);
27 std::make_shared<AudRecA>()->InstallAsService(service_manager);
18 std::make_shared<AudRecU>()->InstallAsService(service_manager); 28 std::make_shared<AudRecU>()->InstallAsService(service_manager);
29 std::make_shared<AudRenA>()->InstallAsService(service_manager);
19 std::make_shared<AudRenU>()->InstallAsService(service_manager); 30 std::make_shared<AudRenU>()->InstallAsService(service_manager);
20 std::make_shared<CodecCtl>()->InstallAsService(service_manager); 31 std::make_shared<CodecCtl>()->InstallAsService(service_manager);
21 std::make_shared<HwOpus>()->InstallAsService(service_manager); 32 std::make_shared<HwOpus>()->InstallAsService(service_manager);
33
34 std::make_shared<AudDbg>("audin:d")->InstallAsService(service_manager);
35 std::make_shared<AudDbg>("audout:d")->InstallAsService(service_manager);
36 std::make_shared<AudDbg>("audrec:d")->InstallAsService(service_manager);
37 std::make_shared<AudDbg>("audren:d")->InstallAsService(service_manager);
22} 38}
23 39
24} // namespace Service::Audio 40} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp
new file mode 100644
index 000000000..bf8d40157
--- /dev/null
+++ b/src/core/hle/service/audio/audout_a.cpp
@@ -0,0 +1,24 @@
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/audio/audout_a.h"
6
7namespace Service::Audio {
8
9AudOutA::AudOutA() : ServiceFramework{"audout:a"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "RequestSuspendAudioOuts"},
13 {1, nullptr, "RequestResumeAudioOuts"},
14 {2, nullptr, "GetAudioOutsProcessMasterVolume"},
15 {3, nullptr, "SetAudioOutsProcessMasterVolume"},
16 {4, nullptr, "GetAudioOutsProcessRecordVolume"},
17 {5, nullptr, "SetAudioOutsProcessRecordVolume"},
18 };
19 // clang-format on
20
21 RegisterHandlers(functions);
22}
23
24} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h
new file mode 100644
index 000000000..91a069152
--- /dev/null
+++ b/src/core/hle/service/audio/audout_a.h
@@ -0,0 +1,16 @@
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::Audio {
10
11class AudOutA final : public ServiceFramework<AudOutA> {
12public:
13 explicit AudOutA();
14};
15
16} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 1dcd84d98..108a7c6eb 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -4,9 +4,10 @@
4 4
5#include <array> 5#include <array>
6#include <vector> 6#include <vector>
7
8#include "audio_core/codec.h"
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "core/core_timing.h" 10#include "core/core.h"
9#include "core/core_timing_util.h"
10#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/event.h" 12#include "core/hle/kernel/event.h"
12#include "core/hle/kernel/hle_ipc.h" 13#include "core/hle/kernel/hle_ipc.h"
@@ -14,17 +15,21 @@
14 15
15namespace Service::Audio { 16namespace Service::Audio {
16 17
17/// Switch sample rate frequency 18namespace ErrCodes {
18constexpr u32 sample_rate{48000}; 19enum {
19/// TODO(st4rk): dynamic number of channels, as I think Switch has support 20 ErrorUnknown = 2,
20/// to more audio channels (probably when Docked I guess) 21 BufferCountExceeded = 8,
21constexpr u32 audio_channels{2}; 22};
22/// TODO(st4rk): find a proper value for the audio_ticks 23}
23constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)}; 24
25constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
26constexpr int DefaultSampleRate{48000};
24 27
25class IAudioOut final : public ServiceFramework<IAudioOut> { 28class IAudioOut final : public ServiceFramework<IAudioOut> {
26public: 29public:
27 IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { 30 IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core)
31 : ServiceFramework("IAudioOut"), audio_params(audio_params), audio_core(audio_core) {
32
28 static const FunctionInfo functions[] = { 33 static const FunctionInfo functions[] = {
29 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 34 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
30 {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, 35 {1, &IAudioOut::StartAudioOut, "StartAudioOut"},
@@ -32,66 +37,65 @@ public:
32 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, 37 {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
33 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, 38 {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
34 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, 39 {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"},
35 {6, nullptr, "ContainsAudioOutBuffer"}, 40 {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
36 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, 41 {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
37 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, 42 {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
38 {9, nullptr, "GetAudioOutBufferCount"}, 43 {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
39 {10, nullptr, "GetAudioOutPlayedSampleCount"}, 44 {10, nullptr, "GetAudioOutPlayedSampleCount"},
40 {11, nullptr, "FlushAudioOutBuffers"}, 45 {11, nullptr, "FlushAudioOutBuffers"},
41 }; 46 };
42 RegisterHandlers(functions); 47 RegisterHandlers(functions);
43 48
44 // This is the event handle used to check if the audio buffer was released 49 // This is the event handle used to check if the audio buffer was released
45 buffer_event = 50 buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
46 Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
47
48 // Register event callback to update the Audio Buffer
49 audio_event = CoreTiming::RegisterEvent(
50 "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
51 UpdateAudioBuffersCallback();
52 CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
53 });
54
55 // Start the audio event
56 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
57 }
58 51
59 ~IAudioOut() { 52 stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
60 CoreTiming::UnscheduleEvent(audio_event, 0); 53 "IAudioOut", [=]() { buffer_event->Signal(); });
61 } 54 }
62 55
63private: 56private:
57 struct AudioBuffer {
58 u64_le next;
59 u64_le buffer;
60 u64_le buffer_capacity;
61 u64_le buffer_size;
62 u64_le offset;
63 };
64 static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
65
64 void GetAudioOutState(Kernel::HLERequestContext& ctx) { 66 void GetAudioOutState(Kernel::HLERequestContext& ctx) {
65 LOG_DEBUG(Service_Audio, "called"); 67 LOG_DEBUG(Service_Audio, "called");
66 IPC::ResponseBuilder rb{ctx, 3}; 68 IPC::ResponseBuilder rb{ctx, 3};
67 rb.Push(RESULT_SUCCESS); 69 rb.Push(RESULT_SUCCESS);
68 rb.Push(static_cast<u32>(audio_out_state)); 70 rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
69 } 71 }
70 72
71 void StartAudioOut(Kernel::HLERequestContext& ctx) { 73 void StartAudioOut(Kernel::HLERequestContext& ctx) {
72 LOG_WARNING(Service_Audio, "(STUBBED) called"); 74 LOG_DEBUG(Service_Audio, "called");
73 75
74 // Start audio 76 if (stream->IsPlaying()) {
75 audio_out_state = AudioState::Started; 77 IPC::ResponseBuilder rb{ctx, 2};
78 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown));
79 return;
80 }
81
82 audio_core.StartStream(stream);
76 83
77 IPC::ResponseBuilder rb{ctx, 2}; 84 IPC::ResponseBuilder rb{ctx, 2};
78 rb.Push(RESULT_SUCCESS); 85 rb.Push(RESULT_SUCCESS);
79 } 86 }
80 87
81 void StopAudioOut(Kernel::HLERequestContext& ctx) { 88 void StopAudioOut(Kernel::HLERequestContext& ctx) {
82 LOG_WARNING(Service_Audio, "(STUBBED) called"); 89 LOG_DEBUG(Service_Audio, "called");
83
84 // Stop audio
85 audio_out_state = AudioState::Stopped;
86 90
87 queue_keys.clear(); 91 audio_core.StopStream(stream);
88 92
89 IPC::ResponseBuilder rb{ctx, 2}; 93 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(RESULT_SUCCESS); 94 rb.Push(RESULT_SUCCESS);
91 } 95 }
92 96
93 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { 97 void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
94 LOG_WARNING(Service_Audio, "(STUBBED) called"); 98 LOG_DEBUG(Service_Audio, "called");
95 99
96 IPC::ResponseBuilder rb{ctx, 2, 1}; 100 IPC::ResponseBuilder rb{ctx, 2, 1};
97 rb.Push(RESULT_SUCCESS); 101 rb.Push(RESULT_SUCCESS);
@@ -99,101 +103,107 @@ private:
99 } 103 }
100 104
101 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 105 void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
102 LOG_WARNING(Service_Audio, "(STUBBED) called"); 106 LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
103 IPC::RequestParser rp{ctx}; 107 IPC::RequestParser rp{ctx};
104 108
105 const u64 key{rp.Pop<u64>()}; 109 const auto& input_buffer{ctx.ReadBuffer()};
106 queue_keys.insert(queue_keys.begin(), key); 110 ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
111 "AudioBuffer input is an invalid size!");
112 AudioBuffer audio_buffer{};
113 std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
114 const u64 tag{rp.Pop<u64>()};
115
116 std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
117 Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
118
119 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded));
122 }
107 123
108 IPC::ResponseBuilder rb{ctx, 2}; 124 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(RESULT_SUCCESS); 125 rb.Push(RESULT_SUCCESS);
110 } 126 }
111 127
112 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { 128 void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
113 LOG_WARNING(Service_Audio, "(STUBBED) called"); 129 LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
114 130 IPC::RequestParser rp{ctx};
115 // TODO(st4rk): This is how libtransistor currently implements the 131 const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
116 // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address 132 const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
117 // is used to know which buffer should be filled with data and send again to the service
118 // through AppendAudioOutBuffer. Check if this is the proper way to do it.
119 u64 key{0};
120
121 if (queue_keys.size()) {
122 key = queue_keys.back();
123 queue_keys.pop_back();
124 }
125 133
126 ctx.WriteBuffer(&key, sizeof(u64)); 134 std::vector<u64> tags{released_buffers};
135 tags.resize(max_count);
136 ctx.WriteBuffer(tags);
127 137
128 IPC::ResponseBuilder rb{ctx, 3}; 138 IPC::ResponseBuilder rb{ctx, 3};
129 rb.Push(RESULT_SUCCESS); 139 rb.Push(RESULT_SUCCESS);
130 // TODO(st4rk): This might be the total of released buffers, needs to be verified on 140 rb.Push<u32>(static_cast<u32>(released_buffers.size()));
131 // hardware
132 rb.Push<u32>(static_cast<u32>(queue_keys.size()));
133 } 141 }
134 142
135 void UpdateAudioBuffersCallback() { 143 void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
136 if (audio_out_state != AudioState::Started) { 144 LOG_DEBUG(Service_Audio, "called");
137 return; 145 IPC::RequestParser rp{ctx};
138 } 146 const u64 tag{rp.Pop<u64>()};
139 147 IPC::ResponseBuilder rb{ctx, 3};
140 if (queue_keys.empty()) { 148 rb.Push(RESULT_SUCCESS);
141 return; 149 rb.Push(stream->ContainsBuffer(tag));
142 } 150 }
143 151
144 buffer_event->Signal(); 152 void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
153 LOG_DEBUG(Service_Audio, "called");
154 IPC::ResponseBuilder rb{ctx, 3};
155 rb.Push(RESULT_SUCCESS);
156 rb.Push(static_cast<u32>(stream->GetQueueSize()));
145 } 157 }
146 158
147 enum class AudioState : u32 { 159 AudioCore::AudioOut& audio_core;
148 Started, 160 AudioCore::StreamPtr stream;
149 Stopped,
150 };
151 161
152 /// This is used to trigger the audio event callback that is going to read the samples from the 162 AudoutParams audio_params{};
153 /// audio_buffer list and enqueue the samples using the sink (audio_core).
154 CoreTiming::EventType* audio_event;
155 163
156 /// This is the evend handle used to check if the audio buffer was released 164 /// This is the evend handle used to check if the audio buffer was released
157 Kernel::SharedPtr<Kernel::Event> buffer_event; 165 Kernel::SharedPtr<Kernel::Event> buffer_event;
158
159 /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
160 /// uses the key as an address in the App, so we need to return when the
161 /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
162 /// libtransistor uses the key returned as an pointer.
163 std::vector<u64> queue_keys;
164
165 AudioState audio_out_state;
166}; 166};
167 167
168void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { 168void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
169 LOG_WARNING(Service_Audio, "(STUBBED) called"); 169 LOG_DEBUG(Service_Audio, "called");
170 IPC::RequestParser rp{ctx}; 170 IPC::RequestParser rp{ctx};
171 171
172 constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; 172 ctx.WriteBuffer(DefaultDevice);
173 ctx.WriteBuffer(audio_interface);
174 173
175 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); 174 IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
176 175
177 rb.Push(RESULT_SUCCESS); 176 rb.Push(RESULT_SUCCESS);
178 // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, 177 rb.Push<u32>(1); // Amount of audio devices
179 // it's highly possible to have more than one interface (despite that libtransistor requires
180 // only one).
181 rb.Push<u32>(1);
182} 178}
183 179
184void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { 180void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
185 LOG_WARNING(Service_Audio, "(STUBBED) called"); 181 LOG_DEBUG(Service_Audio, "called");
186 182
187 if (!audio_out_interface) { 183 ctx.WriteBuffer(DefaultDevice);
188 audio_out_interface = std::make_shared<IAudioOut>(); 184 IPC::RequestParser rp{ctx};
185 auto params{rp.PopRaw<AudoutParams>()};
186 if (params.channel_count <= 2) {
187 // Mono does not exist for audout
188 params.channel_count = 2;
189 } else {
190 params.channel_count = 6;
189 } 191 }
192 if (!params.sample_rate) {
193 params.sample_rate = DefaultSampleRate;
194 }
195
196 // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
197 // will likely need to be updated as well.
198 ASSERT_MSG(!audio_out_interface, "Unimplemented");
199 audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core);
190 200
191 IPC::ResponseBuilder rb{ctx, 6, 0, 1}; 201 IPC::ResponseBuilder rb{ctx, 6, 0, 1};
192 rb.Push(RESULT_SUCCESS); 202 rb.Push(RESULT_SUCCESS);
193 rb.Push<u32>(sample_rate); 203 rb.Push<u32>(DefaultSampleRate);
194 rb.Push<u32>(audio_channels); 204 rb.Push<u32>(params.channel_count);
195 rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); 205 rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
196 rb.Push<u32>(0); // This field is unknown 206 rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
197 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); 207 rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
198} 208}
199 209
@@ -203,6 +213,7 @@ AudOutU::AudOutU() : ServiceFramework("audout:u") {
203 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, 213 {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
204 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}}; 214 {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}};
205 RegisterHandlers(functions); 215 RegisterHandlers(functions);
216 audio_core = std::make_unique<AudioCore::AudioOut>();
206} 217}
207 218
208} // namespace Service::Audio 219} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index 847d86aa6..fd491f65d 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "audio_core/audio_out.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -12,6 +13,18 @@ class HLERequestContext;
12 13
13namespace Service::Audio { 14namespace Service::Audio {
14 15
16struct AudoutParams {
17 s32_le sample_rate;
18 u16_le channel_count;
19 INSERT_PADDING_BYTES(2);
20};
21static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
22
23enum class AudioState : u32 {
24 Started,
25 Stopped,
26};
27
15class IAudioOut; 28class IAudioOut;
16 29
17class AudOutU final : public ServiceFramework<AudOutU> { 30class AudOutU final : public ServiceFramework<AudOutU> {
@@ -21,19 +34,10 @@ public:
21 34
22private: 35private:
23 std::shared_ptr<IAudioOut> audio_out_interface; 36 std::shared_ptr<IAudioOut> audio_out_interface;
37 std::unique_ptr<AudioCore::AudioOut> audio_core;
24 38
25 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); 39 void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
26 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); 40 void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
27
28 enum class PcmFormat : u32 {
29 Invalid = 0,
30 Int8 = 1,
31 Int16 = 2,
32 Int24 = 3,
33 Int32 = 4,
34 PcmFloat = 5,
35 Adpcm = 6,
36 };
37}; 41};
38 42
39} // namespace Service::Audio 43} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp
new file mode 100644
index 000000000..016eabf53
--- /dev/null
+++ b/src/core/hle/service/audio/audrec_a.cpp
@@ -0,0 +1,20 @@
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/audio/audrec_a.h"
6
7namespace Service::Audio {
8
9AudRecA::AudRecA() : ServiceFramework{"audrec:a"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "RequestSuspendFinalOutputRecorders"},
13 {1, nullptr, "RequestResumeFinalOutputRecorders"},
14 };
15 // clang-format on
16
17 RegisterHandlers(functions);
18}
19
20} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h
new file mode 100644
index 000000000..9685047f2
--- /dev/null
+++ b/src/core/hle/service/audio/audrec_a.h
@@ -0,0 +1,16 @@
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::Audio {
10
11class AudRecA final : public ServiceFramework<AudRecA> {
12public:
13 explicit AudRecA();
14};
15
16} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp
new file mode 100644
index 000000000..616ff3dc4
--- /dev/null
+++ b/src/core/hle/service/audio/audren_a.cpp
@@ -0,0 +1,26 @@
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/audio/audren_a.h"
6
7namespace Service::Audio {
8
9AudRenA::AudRenA() : ServiceFramework{"audren:a"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, nullptr, "RequestSuspendAudioRenderers"},
13 {1, nullptr, "RequestResumeAudioRenderers"},
14 {2, nullptr, "GetAudioRenderersProcessMasterVolume"},
15 {3, nullptr, "SetAudioRenderersProcessMasterVolume"},
16 {4, nullptr, "RegisterAppletResourceUserId"},
17 {5, nullptr, "UnregisterAppletResourceUserId"},
18 {6, nullptr, "GetAudioRenderersProcessRecordVolume"},
19 {7, nullptr, "SetAudioRenderersProcessRecordVolume"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24}
25
26} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h
new file mode 100644
index 000000000..5ecf2e184
--- /dev/null
+++ b/src/core/hle/service/audio/audren_a.h
@@ -0,0 +1,16 @@
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::Audio {
10
11class AudRenA final : public ServiceFramework<AudRenA> {
12public:
13 explicit AudRenA();
14};
15
16} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 6aed9e2fa..f99304de5 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -15,13 +15,10 @@
15 15
16namespace Service::Audio { 16namespace Service::Audio {
17 17
18/// TODO(bunnei): Find a proper value for the audio_ticks
19constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)};
20
21class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 18class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
22public: 19public:
23 explicit IAudioRenderer(AudioRendererParameter audren_params) 20 explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params)
24 : ServiceFramework("IAudioRenderer"), worker_params(audren_params) { 21 : ServiceFramework("IAudioRenderer") {
25 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
26 {0, nullptr, "GetAudioRendererSampleRate"}, 23 {0, nullptr, "GetAudioRendererSampleRate"},
27 {1, nullptr, "GetAudioRendererSampleCount"}, 24 {1, nullptr, "GetAudioRendererSampleCount"},
@@ -39,21 +36,8 @@ public:
39 RegisterHandlers(functions); 36 RegisterHandlers(functions);
40 37
41 system_event = 38 system_event =
42 Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); 39 Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
43 40 renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
44 // Register event callback to update the Audio Buffer
45 audio_event = CoreTiming::RegisterEvent(
46 "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) {
47 UpdateAudioCallback();
48 CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
49 });
50
51 // Start the audio event
52 CoreTiming::ScheduleEvent(audio_ticks, audio_event);
53 voice_status_list.resize(worker_params.voice_count);
54 }
55 ~IAudioRenderer() {
56 CoreTiming::UnscheduleEvent(audio_event, 0);
57 } 41 }
58 42
59private: 43private:
@@ -62,60 +46,9 @@ private:
62 } 46 }
63 47
64 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { 48 void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
65 UpdateDataHeader config{}; 49 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
66 auto buf = ctx.ReadBuffer();
67 std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader));
68 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
69
70 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
71 std::memcpy(mem_pool_info.data(),
72 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size,
73 memory_pool_count * sizeof(MemoryPoolInfo));
74
75 std::vector<VoiceInfo> voice_info(worker_params.voice_count);
76 std::memcpy(voice_info.data(),
77 buf.data() + sizeof(UpdateDataHeader) + config.behavior_size +
78 config.memory_pools_size + config.voice_resource_size,
79 worker_params.voice_count * sizeof(VoiceInfo));
80
81 UpdateDataHeader response_data{worker_params};
82
83 ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
84
85 std::vector<u8> output(response_data.total_size);
86 std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader));
87 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
88 for (unsigned i = 0; i < memory_pool.size(); i++) {
89 if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach)
90 memory_pool[i].state = MemoryPoolStates::Attached;
91 else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach)
92 memory_pool[i].state = MemoryPoolStates::Detached;
93 }
94 std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(),
95 response_data.memory_pools_size);
96
97 for (unsigned i = 0; i < voice_info.size(); i++) {
98 if (voice_info[i].is_new) {
99 voice_status_list[i].played_sample_count = 0;
100 voice_status_list[i].wave_buffer_consumed = 0;
101 } else if (voice_info[i].play_state == (u8)PlayStates::Started) {
102 for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) {
103 voice_status_list[i].played_sample_count +=
104 (voice_info[i].wave_buffer[buff_idx].end_sample_offset -
105 voice_info[i].wave_buffer[buff_idx].start_sample_offset) /
106 2;
107 voice_status_list[i].wave_buffer_consumed++;
108 }
109 }
110 }
111 std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size,
112 voice_status_list.data(), response_data.voices_size);
113
114 ctx.WriteBuffer(output);
115
116 IPC::ResponseBuilder rb{ctx, 2}; 50 IPC::ResponseBuilder rb{ctx, 2};
117 rb.Push(RESULT_SUCCESS); 51 rb.Push(RESULT_SUCCESS);
118
119 LOG_WARNING(Service_Audio, "(STUBBED) called"); 52 LOG_WARNING(Service_Audio, "(STUBBED) called");
120 } 53 }
121 54
@@ -136,8 +69,6 @@ private:
136 } 69 }
137 70
138 void QuerySystemEvent(Kernel::HLERequestContext& ctx) { 71 void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
139 // system_event->Signal();
140
141 IPC::ResponseBuilder rb{ctx, 2, 1}; 72 IPC::ResponseBuilder rb{ctx, 2, 1};
142 rb.Push(RESULT_SUCCESS); 73 rb.Push(RESULT_SUCCESS);
143 rb.PushCopyObjects(system_event); 74 rb.PushCopyObjects(system_event);
@@ -145,131 +76,8 @@ private:
145 LOG_WARNING(Service_Audio, "(STUBBED) called"); 76 LOG_WARNING(Service_Audio, "(STUBBED) called");
146 } 77 }
147 78
148 enum class MemoryPoolStates : u32 { // Should be LE
149 Invalid = 0x0,
150 Unknown = 0x1,
151 RequestDetach = 0x2,
152 Detached = 0x3,
153 RequestAttach = 0x4,
154 Attached = 0x5,
155 Released = 0x6,
156 };
157
158 enum class PlayStates : u8 {
159 Started = 0,
160 Stopped = 1,
161 };
162
163 struct MemoryPoolEntry {
164 MemoryPoolStates state;
165 u32_le unknown_4;
166 u32_le unknown_8;
167 u32_le unknown_c;
168 };
169 static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
170
171 struct MemoryPoolInfo {
172 u64_le pool_address;
173 u64_le pool_size;
174 MemoryPoolStates pool_state;
175 INSERT_PADDING_WORDS(3); // Unknown
176 };
177 static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
178
179 struct UpdateDataHeader {
180 UpdateDataHeader() {}
181
182 explicit UpdateDataHeader(const AudioRendererParameter& config) {
183 revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
184 behavior_size = 0xb0;
185 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
186 voices_size = config.voice_count * 0x10;
187 voice_resource_size = 0x0;
188 effects_size = config.effect_count * 0x10;
189 mixes_size = 0x0;
190 sinks_size = config.sink_count * 0x20;
191 performance_manager_size = 0x10;
192 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size +
193 voices_size + effects_size + sinks_size + performance_manager_size;
194 }
195
196 u32_le revision;
197 u32_le behavior_size;
198 u32_le memory_pools_size;
199 u32_le voices_size;
200 u32_le voice_resource_size;
201 u32_le effects_size;
202 u32_le mixes_size;
203 u32_le sinks_size;
204 u32_le performance_manager_size;
205 INSERT_PADDING_WORDS(6);
206 u32_le total_size;
207 };
208 static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
209
210 struct BiquadFilter {
211 u8 enable;
212 INSERT_PADDING_BYTES(1);
213 s16_le numerator[3];
214 s16_le denominator[2];
215 };
216 static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
217
218 struct WaveBuffer {
219 u64_le buffer_addr;
220 u64_le buffer_sz;
221 s32_le start_sample_offset;
222 s32_le end_sample_offset;
223 u8 loop;
224 u8 end_of_stream;
225 u8 sent_to_server;
226 INSERT_PADDING_BYTES(5);
227 u64 context_addr;
228 u64 context_sz;
229 INSERT_PADDING_BYTES(8);
230 };
231 static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
232
233 struct VoiceInfo {
234 u32_le id;
235 u32_le node_id;
236 u8 is_new;
237 u8 is_in_use;
238 u8 play_state;
239 u8 sample_format;
240 u32_le sample_rate;
241 u32_le priority;
242 u32_le sorting_order;
243 u32_le channel_count;
244 float_le pitch;
245 float_le volume;
246 BiquadFilter biquad_filter[2];
247 u32_le wave_buffer_count;
248 u16_le wave_buffer_head;
249 INSERT_PADDING_BYTES(6);
250 u64_le additional_params_addr;
251 u64_le additional_params_sz;
252 u32_le mix_id;
253 u32_le splitter_info_id;
254 WaveBuffer wave_buffer[4];
255 u32_le voice_channel_resource_ids[6];
256 INSERT_PADDING_BYTES(24);
257 };
258 static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
259
260 struct VoiceOutStatus {
261 u64_le played_sample_count;
262 u32_le wave_buffer_consumed;
263 INSERT_PADDING_WORDS(1);
264 };
265 static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
266
267 /// This is used to trigger the audio event callback.
268 CoreTiming::EventType* audio_event;
269
270 Kernel::SharedPtr<Kernel::Event> system_event; 79 Kernel::SharedPtr<Kernel::Event> system_event;
271 AudioRendererParameter worker_params; 80 std::unique_ptr<AudioCore::AudioRenderer> renderer;
272 std::vector<VoiceOutStatus> voice_status_list;
273}; 81};
274 82
275class IAudioDevice final : public ServiceFramework<IAudioDevice> { 83class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
368 176
369void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { 177void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
370 IPC::RequestParser rp{ctx}; 178 IPC::RequestParser rp{ctx};
371 auto params = rp.PopRaw<AudioRendererParameter>(); 179 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
372 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 180 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
373 181
374 rb.Push(RESULT_SUCCESS); 182 rb.Push(RESULT_SUCCESS);
@@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
379 187
380void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { 188void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
381 IPC::RequestParser rp{ctx}; 189 IPC::RequestParser rp{ctx};
382 auto params = rp.PopRaw<AudioRendererParameter>(); 190 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
383 191
384 u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); 192 u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40);
385 buffer_sz += params.unknown_c * 1024; 193 buffer_sz += params.unknown_c * 1024;
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index b9b81db4f..14907f8ae 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "audio_core/audio_renderer.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Kernel { 10namespace Kernel {
@@ -12,24 +13,6 @@ class HLERequestContext;
12 13
13namespace Service::Audio { 14namespace Service::Audio {
14 15
15struct AudioRendererParameter {
16 u32_le sample_rate;
17 u32_le sample_count;
18 u32_le unknown_8;
19 u32_le unknown_c;
20 u32_le voice_count;
21 u32_le sink_count;
22 u32_le effect_count;
23 u32_le unknown_1c;
24 u8 unknown_20;
25 INSERT_PADDING_BYTES(3);
26 u32_le splitter_count;
27 u32_le unknown_2c;
28 INSERT_PADDING_WORDS(1);
29 u32_le revision;
30};
31static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
32
33class AudRenU final : public ServiceFramework<AudRenU> { 16class AudRenU final : public ServiceFramework<AudRenU> {
34public: 17public:
35 explicit AudRenU(); 18 explicit AudRenU();
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 844df382c..371cd4997 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.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 <cstring>
6#include <opus.h>
5#include "common/logging/log.h" 7#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/hle_ipc.h" 9#include "core/hle/kernel/hle_ipc.h"
@@ -9,19 +11,142 @@
9 11
10namespace Service::Audio { 12namespace Service::Audio {
11 13
14struct OpusDeleter {
15 void operator()(void* ptr) const {
16 operator delete(ptr);
17 }
18};
19
20class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
21public:
22 IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
23 u32 channel_count)
24 : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
25 sample_rate(sample_rate), channel_count(channel_count) {
26 static const FunctionInfo functions[] = {
27 {0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
28 {1, nullptr, "SetContext"},
29 {2, nullptr, "DecodeInterleavedForMultiStream"},
30 {3, nullptr, "SetContextForMultiStream"},
31 {4, nullptr, "Unknown4"},
32 {5, nullptr, "Unknown5"},
33 {6, nullptr, "Unknown6"},
34 {7, nullptr, "Unknown7"},
35 };
36 RegisterHandlers(functions);
37 }
38
39private:
40 void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
41 u32 consumed = 0;
42 u32 sample_count = 0;
43 std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
44 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) {
45 IPC::ResponseBuilder rb{ctx, 2};
46 // TODO(ogniK): Use correct error code
47 rb.Push(ResultCode(-1));
48 return;
49 }
50 IPC::ResponseBuilder rb{ctx, 4};
51 rb.Push(RESULT_SUCCESS);
52 rb.Push<u32>(consumed);
53 rb.Push<u32>(sample_count);
54 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
55 }
56
57 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input,
58 std::vector<opus_int16>& output) {
59 size_t raw_output_sz = output.size() * sizeof(opus_int16);
60 if (sizeof(OpusHeader) > input.size())
61 return false;
62 OpusHeader hdr{};
63 std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
64 if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
65 return false;
66 }
67 auto frame = input.data() + sizeof(OpusHeader);
68 auto decoded_sample_count = opus_packet_get_nb_samples(
69 frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
70 static_cast<opus_int32>(sample_rate));
71 if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz)
72 return false;
73 auto out_sample_count =
74 opus_decode(decoder.get(), frame, hdr.sz, output.data(),
75 (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
76 if (out_sample_count < 0)
77 return false;
78 sample_count = out_sample_count;
79 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
80 return true;
81 }
82
83 struct OpusHeader {
84 u32_be sz; // Needs to be BE for some odd reason
85 INSERT_PADDING_WORDS(1);
86 };
87 static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
88
89 std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
90 u32 sample_rate;
91 u32 channel_count;
92};
93
94static size_t WorkerBufferSize(u32 channel_count) {
95 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
96 return opus_decoder_get_size(static_cast<int>(channel_count));
97}
98
12void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { 99void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
13 LOG_WARNING(Service_Audio, "(STUBBED) called"); 100 IPC::RequestParser rp{ctx};
101 auto sample_rate = rp.Pop<u32>();
102 auto channel_count = rp.Pop<u32>();
103 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
104 sample_rate == 12000 || sample_rate == 8000,
105 "Invalid sample rate");
106 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
107 u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count));
108 LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz);
109
14 IPC::ResponseBuilder rb{ctx, 3}; 110 IPC::ResponseBuilder rb{ctx, 3};
15 rb.Push(RESULT_SUCCESS); 111 rb.Push(RESULT_SUCCESS);
16 rb.Push<u32>(0x4000); 112 rb.Push<u32>(worker_buffer_sz);
113}
114
115void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
116 IPC::RequestParser rp{ctx};
117 auto sample_rate = rp.Pop<u32>();
118 auto channel_count = rp.Pop<u32>();
119 auto buffer_sz = rp.Pop<u32>();
120 LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
121 channel_count, buffer_sz);
122 ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
123 sample_rate == 12000 || sample_rate == 8000,
124 "Invalid sample rate");
125 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
126
127 size_t worker_sz = WorkerBufferSize(channel_count);
128 ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
129 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
130 static_cast<OpusDecoder*>(operator new(worker_sz))};
131 if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
132 IPC::ResponseBuilder rb{ctx, 2};
133 // TODO(ogniK): Use correct error code
134 rb.Push(ResultCode(-1));
135 return;
136 }
137
138 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
139 rb.Push(RESULT_SUCCESS);
140 rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate,
141 channel_count);
17} 142}
18 143
19HwOpus::HwOpus() : ServiceFramework("hwopus") { 144HwOpus::HwOpus() : ServiceFramework("hwopus") {
20 static const FunctionInfo functions[] = { 145 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"}, 146 {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"},
22 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, 147 {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"},
23 {2, nullptr, "InitializeMultiStream"}, 148 {2, nullptr, "OpenOpusDecoderForMultiStream"},
24 {3, nullptr, "GetWorkBufferSizeMultiStream"}, 149 {3, nullptr, "GetWorkBufferSizeForMultiStream"},
25 }; 150 };
26 RegisterHandlers(functions); 151 RegisterHandlers(functions);
27} 152}
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index 090b8c825..5258d59f3 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -14,6 +14,7 @@ public:
14 ~HwOpus() = default; 14 ~HwOpus() = default;
15 15
16private: 16private:
17 void OpenOpusDecoder(Kernel::HLERequestContext& ctx);
17 void GetWorkBufferSize(Kernel::HLERequestContext& ctx); 18 void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
18}; 19};
19 20
diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp
new file mode 100644
index 000000000..1c1ecdb60
--- /dev/null
+++ b/src/core/hle/service/bpc/bpc.cpp
@@ -0,0 +1,57 @@
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/bpc/bpc.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::BPC {
12
13class BPC final : public ServiceFramework<BPC> {
14public:
15 explicit BPC() : ServiceFramework{"bpc"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "ShutdownSystem"},
19 {1, nullptr, "RebootSystem"},
20 {2, nullptr, "GetWakeupReason"},
21 {3, nullptr, "GetShutdownReason"},
22 {4, nullptr, "GetAcOk"},
23 {5, nullptr, "GetBoardPowerControlEvent"},
24 {6, nullptr, "GetSleepButtonState"},
25 {7, nullptr, "GetPowerEvent"},
26 {8, nullptr, "Unknown1"},
27 {9, nullptr, "Unknown2"},
28 {10, nullptr, "Unknown3"},
29 };
30 // clang-format on
31
32 RegisterHandlers(functions);
33 }
34};
35
36class BPC_R final : public ServiceFramework<BPC_R> {
37public:
38 explicit BPC_R() : ServiceFramework{"bpc:r"} {
39 // clang-format off
40 static const FunctionInfo functions[] = {
41 {0, nullptr, "GetExternalRtcValue"},
42 {1, nullptr, "SetExternalRtcValue"},
43 {2, nullptr, "ReadExternalRtcResetFlag"},
44 {3, nullptr, "ClearExternalRtcResetFlag"},
45 };
46 // clang-format on
47
48 RegisterHandlers(functions);
49 }
50};
51
52void InstallInterfaces(SM::ServiceManager& sm) {
53 std::make_shared<BPC>()->InstallAsService(sm);
54 std::make_shared<BPC_R>()->InstallAsService(sm);
55}
56
57} // namespace Service::BPC
diff --git a/src/core/hle/service/bpc/bpc.h b/src/core/hle/service/bpc/bpc.h
new file mode 100644
index 000000000..eaa37be8d
--- /dev/null
+++ b/src/core/hle/service/bpc/bpc.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::BPC {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::BPC
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
new file mode 100644
index 000000000..d0a15cc4c
--- /dev/null
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -0,0 +1,72 @@
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/btdrv/btdrv.h"
6#include "core/hle/service/service.h"
7#include "core/hle/service/sm/sm.h"
8
9namespace Service::BtDrv {
10
11class BtDrv final : public ServiceFramework<BtDrv> {
12public:
13 explicit BtDrv() : ServiceFramework{"btdrv"} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, nullptr, "Unknown"},
17 {1, nullptr, "Init"},
18 {2, nullptr, "Enable"},
19 {3, nullptr, "Disable"},
20 {4, nullptr, "CleanupAndShutdown"},
21 {5, nullptr, "GetAdapterProperties"},
22 {6, nullptr, "GetAdapterProperty"},
23 {7, nullptr, "SetAdapterProperty"},
24 {8, nullptr, "StartDiscovery"},
25 {9, nullptr, "CancelDiscovery"},
26 {10, nullptr, "CreateBond"},
27 {11, nullptr, "RemoveBond"},
28 {12, nullptr, "CancelBond"},
29 {13, nullptr, "PinReply"},
30 {14, nullptr, "SspReply"},
31 {15, nullptr, "Unknown2"},
32 {16, nullptr, "InitInterfaces"},
33 {17, nullptr, "HidHostInterface_Connect"},
34 {18, nullptr, "HidHostInterface_Disconnect"},
35 {19, nullptr, "HidHostInterface_SendData"},
36 {20, nullptr, "HidHostInterface_SendData2"},
37 {21, nullptr, "HidHostInterface_SetReport"},
38 {22, nullptr, "HidHostInterface_GetReport"},
39 {23, nullptr, "HidHostInterface_WakeController"},
40 {24, nullptr, "HidHostInterface_AddPairedDevice"},
41 {25, nullptr, "HidHostInterface_GetPairedDevice"},
42 {26, nullptr, "HidHostInterface_CleanupAndShutdown"},
43 {27, nullptr, "Unknown3"},
44 {28, nullptr, "ExtInterface_SetTSI"},
45 {29, nullptr, "ExtInterface_SetBurstMode"},
46 {30, nullptr, "ExtInterface_SetZeroRetran"},
47 {31, nullptr, "ExtInterface_SetMcMode"},
48 {32, nullptr, "ExtInterface_StartLlrMode"},
49 {33, nullptr, "ExtInterface_ExitLlrMode"},
50 {34, nullptr, "ExtInterface_SetRadio"},
51 {35, nullptr, "ExtInterface_SetVisibility"},
52 {36, nullptr, "Unknown4"},
53 {37, nullptr, "Unknown5"},
54 {38, nullptr, "HidHostInterface_GetLatestPlr"},
55 {39, nullptr, "ExtInterface_GetPendingConnections"},
56 {40, nullptr, "HidHostInterface_GetChannelMap"},
57 {41, nullptr, "SetIsBluetoothBoostEnabled"},
58 {42, nullptr, "GetIsBluetoothBoostEnabled"},
59 {43, nullptr, "SetIsBluetoothAfhEnabled"},
60 {44, nullptr, "GetIsBluetoothAfhEnabled"},
61 };
62 // clang-format on
63
64 RegisterHandlers(functions);
65 }
66};
67
68void InstallInterfaces(SM::ServiceManager& sm) {
69 std::make_shared<BtDrv>()->InstallAsService(sm);
70}
71
72} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h
new file mode 100644
index 000000000..164e56f43
--- /dev/null
+++ b/src/core/hle/service/btdrv/btdrv.h
@@ -0,0 +1,16 @@
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::BtDrv {
12
13/// Registers all BtDrv services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm);
15
16} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
new file mode 100644
index 000000000..b949bfabd
--- /dev/null
+++ b/src/core/hle/service/btm/btm.cpp
@@ -0,0 +1,121 @@
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/kernel/hle_ipc.h"
10#include "core/hle/service/btm/btm.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::BTM {
15
16class BTM final : public ServiceFramework<BTM> {
17public:
18 explicit BTM() : ServiceFramework{"btm"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Unknown1"},
22 {1, nullptr, "Unknown2"},
23 {2, nullptr, "RegisterSystemEventForConnectedDeviceConditionImpl"},
24 {3, nullptr, "Unknown3"},
25 {4, nullptr, "Unknown4"},
26 {5, nullptr, "Unknown5"},
27 {6, nullptr, "Unknown6"},
28 {7, nullptr, "Unknown7"},
29 {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfoImpl"},
30 {9, nullptr, "Unknown8"},
31 {10, nullptr, "Unknown9"},
32 {11, nullptr, "Unknown10"},
33 {12, nullptr, "Unknown11"},
34 {13, nullptr, "Unknown12"},
35 {14, nullptr, "EnableRadioImpl"},
36 {15, nullptr, "DisableRadioImpl"},
37 {16, nullptr, "Unknown13"},
38 {17, nullptr, "Unknown14"},
39 {18, nullptr, "Unknown15"},
40 {19, nullptr, "Unknown16"},
41 {20, nullptr, "Unknown17"},
42 {21, nullptr, "Unknown18"},
43 };
44 // clang-format on
45
46 RegisterHandlers(functions);
47 }
48};
49
50class BTM_DBG final : public ServiceFramework<BTM_DBG> {
51public:
52 explicit BTM_DBG() : ServiceFramework{"btm:dbg"} {
53 // clang-format off
54 static const FunctionInfo functions[] = {
55 {0, nullptr, "RegisterSystemEventForDiscoveryImpl"},
56 {1, nullptr, "Unknown1"},
57 {2, nullptr, "Unknown2"},
58 {3, nullptr, "Unknown3"},
59 {4, nullptr, "Unknown4"},
60 {5, nullptr, "Unknown5"},
61 {6, nullptr, "Unknown6"},
62 {7, nullptr, "Unknown7"},
63 {8, nullptr, "Unknown8"},
64 };
65 // clang-format on
66
67 RegisterHandlers(functions);
68 }
69};
70
71class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> {
72public:
73 explicit IBtmSystemCore() : ServiceFramework{"IBtmSystemCore"} {
74 // clang-format off
75 static const FunctionInfo functions[] = {
76 {0, nullptr, "StartGamepadPairingImpl"},
77 {1, nullptr, "CancelGamepadPairingImpl"},
78 {2, nullptr, "ClearGamepadPairingDatabaseImpl"},
79 {3, nullptr, "GetPairedGamepadCountImpl"},
80 {4, nullptr, "EnableRadioImpl"},
81 {5, nullptr, "DisableRadioImpl"},
82 {6, nullptr, "GetRadioOnOffImpl"},
83 {7, nullptr, "AcquireRadioEventImpl"},
84 {8, nullptr, "AcquireGamepadPairingEventImpl"},
85 {9, nullptr, "IsGamepadPairingStartedImpl"},
86 };
87 // clang-format on
88
89 RegisterHandlers(functions);
90 }
91};
92
93class BTM_SYS final : public ServiceFramework<BTM_SYS> {
94public:
95 explicit BTM_SYS() : ServiceFramework{"btm:sys"} {
96 // clang-format off
97 static const FunctionInfo functions[] = {
98 {0, &BTM_SYS::GetCoreImpl, "GetCoreImpl"},
99 };
100 // clang-format on
101
102 RegisterHandlers(functions);
103 }
104
105private:
106 void GetCoreImpl(Kernel::HLERequestContext& ctx) {
107 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
108 rb.Push(RESULT_SUCCESS);
109 rb.PushIpcInterface<IBtmSystemCore>();
110
111 LOG_DEBUG(Service_BTM, "called");
112 }
113};
114
115void InstallInterfaces(SM::ServiceManager& sm) {
116 std::make_shared<BTM>()->InstallAsService(sm);
117 std::make_shared<BTM_DBG>()->InstallAsService(sm);
118 std::make_shared<BTM_SYS>()->InstallAsService(sm);
119}
120
121} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h
new file mode 100644
index 000000000..e6425a7e3
--- /dev/null
+++ b/src/core/hle/service/btm/btm.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::BTM {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::BTM
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
new file mode 100644
index 000000000..ae7b0720b
--- /dev/null
+++ b/src/core/hle/service/caps/caps.cpp
@@ -0,0 +1,152 @@
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/caps/caps.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::Capture {
12
13class CAPS_A final : public ServiceFramework<CAPS_A> {
14public:
15 explicit CAPS_A() : ServiceFramework{"caps:a"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "Unknown1"},
19 {1, nullptr, "Unknown2"},
20 {2, nullptr, "Unknown3"},
21 {3, nullptr, "Unknown4"},
22 {4, nullptr, "Unknown5"},
23 {5, nullptr, "Unknown6"},
24 {6, nullptr, "Unknown7"},
25 {7, nullptr, "Unknown8"},
26 {8, nullptr, "Unknown9"},
27 {9, nullptr, "Unknown10"},
28 {10, nullptr, "Unknown11"},
29 {11, nullptr, "Unknown12"},
30 {12, nullptr, "Unknown13"},
31 {13, nullptr, "Unknown14"},
32 {14, nullptr, "Unknown15"},
33 {301, nullptr, "Unknown16"},
34 {401, nullptr, "Unknown17"},
35 {501, nullptr, "Unknown18"},
36 {1001, nullptr, "Unknown19"},
37 {1002, nullptr, "Unknown20"},
38 {8001, nullptr, "Unknown21"},
39 {8002, nullptr, "Unknown22"},
40 {8011, nullptr, "Unknown23"},
41 {8012, nullptr, "Unknown24"},
42 {8021, nullptr, "Unknown25"},
43 {10011, nullptr, "Unknown26"},
44 };
45 // clang-format on
46
47 RegisterHandlers(functions);
48 }
49};
50
51class CAPS_C final : public ServiceFramework<CAPS_C> {
52public:
53 explicit CAPS_C() : ServiceFramework{"caps:c"} {
54 // clang-format off
55 static const FunctionInfo functions[] = {
56 {2001, nullptr, "Unknown1"},
57 {2002, nullptr, "Unknown2"},
58 {2011, nullptr, "Unknown3"},
59 {2012, nullptr, "Unknown4"},
60 {2013, nullptr, "Unknown5"},
61 {2014, nullptr, "Unknown6"},
62 {2101, nullptr, "Unknown7"},
63 {2102, nullptr, "Unknown8"},
64 {2201, nullptr, "Unknown9"},
65 {2301, nullptr, "Unknown10"},
66 };
67 // clang-format on
68
69 RegisterHandlers(functions);
70 }
71};
72
73class CAPS_SC final : public ServiceFramework<CAPS_SC> {
74public:
75 explicit CAPS_SC() : ServiceFramework{"caps:sc"} {
76 // clang-format off
77 static const FunctionInfo functions[] = {
78 {1, nullptr, "Unknown1"},
79 {2, nullptr, "Unknown2"},
80 {1001, nullptr, "Unknown3"},
81 {1002, nullptr, "Unknown4"},
82 {1003, nullptr, "Unknown5"},
83 {1011, nullptr, "Unknown6"},
84 {1012, nullptr, "Unknown7"},
85 {1201, nullptr, "Unknown8"},
86 {1202, nullptr, "Unknown9"},
87 {1203, nullptr, "Unknown10"},
88 };
89 // clang-format on
90
91 RegisterHandlers(functions);
92 }
93};
94
95class CAPS_SS final : public ServiceFramework<CAPS_SS> {
96public:
97 explicit CAPS_SS() : ServiceFramework{"caps:ss"} {
98 // clang-format off
99 static const FunctionInfo functions[] = {
100 {201, nullptr, "Unknown1"},
101 {202, nullptr, "Unknown2"},
102 {203, nullptr, "Unknown3"},
103 {204, nullptr, "Unknown4"},
104 };
105 // clang-format on
106
107 RegisterHandlers(functions);
108 }
109};
110
111class CAPS_SU final : public ServiceFramework<CAPS_SU> {
112public:
113 explicit CAPS_SU() : ServiceFramework{"caps:su"} {
114 // clang-format off
115 static const FunctionInfo functions[] = {
116 {201, nullptr, "SaveScreenShot"},
117 {203, nullptr, "SaveScreenShotEx0"},
118 };
119 // clang-format on
120
121 RegisterHandlers(functions);
122 }
123};
124
125class CAPS_U final : public ServiceFramework<CAPS_U> {
126public:
127 explicit CAPS_U() : ServiceFramework{"caps:u"} {
128 // clang-format off
129 static const FunctionInfo functions[] = {
130 {102, nullptr, "GetAlbumFileListByAruid"},
131 {103, nullptr, "DeleteAlbumFileByAruid"},
132 {104, nullptr, "GetAlbumFileSizeByAruid"},
133 {110, nullptr, "LoadAlbumScreenShotImageByAruid"},
134 {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
135 {60002, nullptr, "OpenAccessorSessionForApplication"},
136 };
137 // clang-format on
138
139 RegisterHandlers(functions);
140 }
141};
142
143void InstallInterfaces(SM::ServiceManager& sm) {
144 std::make_shared<CAPS_A>()->InstallAsService(sm);
145 std::make_shared<CAPS_C>()->InstallAsService(sm);
146 std::make_shared<CAPS_SC>()->InstallAsService(sm);
147 std::make_shared<CAPS_SS>()->InstallAsService(sm);
148 std::make_shared<CAPS_SU>()->InstallAsService(sm);
149 std::make_shared<CAPS_U>()->InstallAsService(sm);
150}
151
152} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
new file mode 100644
index 000000000..471185dfa
--- /dev/null
+++ b/src/core/hle/service/caps/caps.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::Capture {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::Capture
diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp
new file mode 100644
index 000000000..566fbf924
--- /dev/null
+++ b/src/core/hle/service/fgm/fgm.cpp
@@ -0,0 +1,75 @@
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/ipc_helpers.h"
8#include "core/hle/kernel/hle_ipc.h"
9#include "core/hle/service/fgm/fgm.h"
10#include "core/hle/service/service.h"
11#include "core/hle/service/sm/sm.h"
12
13namespace Service::FGM {
14
15class IRequest final : public ServiceFramework<IRequest> {
16public:
17 explicit IRequest() : ServiceFramework{"IRequest"} {
18 // clang-format off
19 static const FunctionInfo functions[] = {
20 {0, nullptr, "Initialize"},
21 {1, nullptr, "Set"},
22 {2, nullptr, "Get"},
23 {3, nullptr, "Cancel"},
24 };
25 // clang-format on
26
27 RegisterHandlers(functions);
28 }
29};
30
31class FGM final : public ServiceFramework<FGM> {
32public:
33 explicit FGM(const char* name) : ServiceFramework{name} {
34 // clang-format off
35 static const FunctionInfo functions[] = {
36 {0, &FGM::Initialize, "Initialize"},
37 };
38 // clang-format on
39
40 RegisterHandlers(functions);
41 }
42
43private:
44 void Initialize(Kernel::HLERequestContext& ctx) {
45 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
46 rb.Push(RESULT_SUCCESS);
47 rb.PushIpcInterface<IRequest>();
48
49 LOG_DEBUG(Service_FGM, "called");
50 }
51};
52
53class FGM_DBG final : public ServiceFramework<FGM_DBG> {
54public:
55 explicit FGM_DBG() : ServiceFramework{"fgm:dbg"} {
56 // clang-format off
57 static const FunctionInfo functions[] = {
58 {0, nullptr, "Initialize"},
59 {1, nullptr, "Read"},
60 {2, nullptr, "Cancel"},
61 };
62 // clang-format on
63
64 RegisterHandlers(functions);
65 }
66};
67
68void InstallInterfaces(SM::ServiceManager& sm) {
69 std::make_shared<FGM>("fgm")->InstallAsService(sm);
70 std::make_shared<FGM>("fgm:0")->InstallAsService(sm);
71 std::make_shared<FGM>("fgm:9")->InstallAsService(sm);
72 std::make_shared<FGM_DBG>()->InstallAsService(sm);
73}
74
75} // namespace Service::FGM
diff --git a/src/core/hle/service/fgm/fgm.h b/src/core/hle/service/fgm/fgm.h
new file mode 100644
index 000000000..e59691264
--- /dev/null
+++ b/src/core/hle/service/fgm/fgm.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::FGM {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::FGM
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index fdd2fda18..e17d637e4 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -14,6 +14,8 @@
14#include "core/file_sys/vfs_offset.h" 14#include "core/file_sys/vfs_offset.h"
15#include "core/file_sys/vfs_real.h" 15#include "core/file_sys/vfs_real.h"
16#include "core/hle/service/filesystem/filesystem.h" 16#include "core/hle/service/filesystem/filesystem.h"
17#include "core/hle/service/filesystem/fsp_ldr.h"
18#include "core/hle/service/filesystem/fsp_pr.h"
17#include "core/hle/service/filesystem/fsp_srv.h" 19#include "core/hle/service/filesystem/fsp_srv.h"
18 20
19namespace Service::FileSystem { 21namespace Service::FileSystem {
@@ -298,6 +300,8 @@ void RegisterFileSystems() {
298 300
299void InstallInterfaces(SM::ServiceManager& service_manager) { 301void InstallInterfaces(SM::ServiceManager& service_manager) {
300 RegisterFileSystems(); 302 RegisterFileSystems();
303 std::make_shared<FSP_LDR>()->InstallAsService(service_manager);
304 std::make_shared<FSP_PR>()->InstallAsService(service_manager);
301 std::make_shared<FSP_SRV>()->InstallAsService(service_manager); 305 std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
302} 306}
303 307
diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp
new file mode 100644
index 000000000..0ab9c2606
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp_ldr.cpp
@@ -0,0 +1,22 @@
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/filesystem/fsp_ldr.h"
6#include "core/hle/service/service.h"
7
8namespace Service::FileSystem {
9
10FSP_LDR::FSP_LDR() : ServiceFramework{"fsp:ldr"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "OpenCodeFileSystem"},
14 {1, nullptr, "IsArchivedProgram"},
15 {2, nullptr, "SetCurrentProcess"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h
new file mode 100644
index 000000000..fa8e11b4c
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp_ldr.h
@@ -0,0 +1,16 @@
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::FileSystem {
10
11class FSP_LDR final : public ServiceFramework<FSP_LDR> {
12public:
13 explicit FSP_LDR();
14};
15
16} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp
new file mode 100644
index 000000000..32b0ae454
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp_pr.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/filesystem/fsp_pr.h"
6#include "core/hle/service/service.h"
7
8namespace Service::FileSystem {
9
10FSP_PR::FSP_PR() : ServiceFramework{"fsp:pr"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, nullptr, "RegisterProgram"},
14 {1, nullptr, "UnregisterProgram"},
15 {2, nullptr, "SetCurrentProcess"},
16 {256, nullptr, "SetEnabledProgramVerification"},
17 };
18 // clang-format on
19
20 RegisterHandlers(functions);
21}
22
23} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h
new file mode 100644
index 000000000..62edcd08a
--- /dev/null
+++ b/src/core/hle/service/filesystem/fsp_pr.h
@@ -0,0 +1,16 @@
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::FileSystem {
10
11class FSP_PR final : public ServiceFramework<FSP_PR> {
12public:
13 explicit FSP_PR();
14};
15
16} // namespace Service::FileSystem
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index e4619a547..8f0262e34 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -326,7 +326,7 @@ public:
326 {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, 326 {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
327 {80, nullptr, "GetGyroscopeZeroDriftMode"}, 327 {80, nullptr, "GetGyroscopeZeroDriftMode"},
328 {81, nullptr, "ResetGyroscopeZeroDriftMode"}, 328 {81, nullptr, "ResetGyroscopeZeroDriftMode"},
329 {82, nullptr, "IsSixAxisSensorAtRest"}, 329 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
330 {91, nullptr, "ActivateGesture"}, 330 {91, nullptr, "ActivateGesture"},
331 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 331 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
332 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 332 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -337,13 +337,14 @@ public:
337 "AcquireNpadStyleSetUpdateEventHandle"}, 337 "AcquireNpadStyleSetUpdateEventHandle"},
338 {107, nullptr, "DisconnectNpad"}, 338 {107, nullptr, "DisconnectNpad"},
339 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, 339 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
340 {109, nullptr, "ActivateNpadWithRevision"},
340 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, 341 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
341 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, 342 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
342 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, 343 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault,
343 "SetNpadJoyAssignmentModeSingleByDefault"}, 344 "SetNpadJoyAssignmentModeSingleByDefault"},
344 {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"}, 345 {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"},
345 {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, 346 {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
346 {125, nullptr, "MergeSingleJoyAsDualJoy"}, 347 {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
347 {126, nullptr, "StartLrAssignmentMode"}, 348 {126, nullptr, "StartLrAssignmentMode"},
348 {127, nullptr, "StopLrAssignmentMode"}, 349 {127, nullptr, "StopLrAssignmentMode"},
349 {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, 350 {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
@@ -455,6 +456,14 @@ private:
455 LOG_WARNING(Service_HID, "(STUBBED) called"); 456 LOG_WARNING(Service_HID, "(STUBBED) called");
456 } 457 }
457 458
459 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
460 IPC::ResponseBuilder rb{ctx, 2};
461 rb.Push(RESULT_SUCCESS);
462 // TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
463 rb.Push(true);
464 LOG_WARNING(Service_HID, "(STUBBED) called");
465 }
466
458 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 467 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
459 IPC::ResponseBuilder rb{ctx, 2}; 468 IPC::ResponseBuilder rb{ctx, 2};
460 rb.Push(RESULT_SUCCESS); 469 rb.Push(RESULT_SUCCESS);
@@ -530,6 +539,12 @@ private:
530 LOG_WARNING(Service_HID, "(STUBBED) called"); 539 LOG_WARNING(Service_HID, "(STUBBED) called");
531 } 540 }
532 541
542 void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
543 IPC::ResponseBuilder rb{ctx, 2};
544 rb.Push(RESULT_SUCCESS);
545 LOG_WARNING(Service_HID, "(STUBBED) called");
546 }
547
533 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { 548 void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
534 IPC::ResponseBuilder rb{ctx, 2}; 549 IPC::ResponseBuilder rb{ctx, 2};
535 rb.Push(RESULT_SUCCESS); 550 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
new file mode 100644
index 000000000..8fc8b1057
--- /dev/null
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -0,0 +1,90 @@
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/kernel/hle_ipc.h"
10#include "core/hle/service/lbl/lbl.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::LBL {
15
16class LBL final : public ServiceFramework<LBL> {
17public:
18 explicit LBL() : ServiceFramework{"lbl"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Unknown1"},
22 {1, nullptr, "Unknown2"},
23 {2, nullptr, "Unknown3"},
24 {3, nullptr, "Unknown4"},
25 {4, nullptr, "Unknown5"},
26 {5, nullptr, "Unknown6"},
27 {6, nullptr, "TurnOffBacklight"},
28 {7, nullptr, "TurnOnBacklight"},
29 {8, nullptr, "GetBacklightStatus"},
30 {9, nullptr, "Unknown7"},
31 {10, nullptr, "Unknown8"},
32 {11, nullptr, "Unknown9"},
33 {12, nullptr, "Unknown10"},
34 {13, nullptr, "Unknown11"},
35 {14, nullptr, "Unknown12"},
36 {15, nullptr, "Unknown13"},
37 {16, nullptr, "ReadRawLightSensor"},
38 {17, nullptr, "Unknown14"},
39 {18, nullptr, "Unknown15"},
40 {19, nullptr, "Unknown16"},
41 {20, nullptr, "Unknown17"},
42 {21, nullptr, "Unknown18"},
43 {22, nullptr, "Unknown19"},
44 {23, nullptr, "Unknown20"},
45 {24, nullptr, "Unknown21"},
46 {25, nullptr, "Unknown22"},
47 {26, &LBL::EnableVrMode, "EnableVrMode"},
48 {27, &LBL::DisableVrMode, "DisableVrMode"},
49 {28, &LBL::GetVrMode, "GetVrMode"},
50 };
51 // clang-format on
52
53 RegisterHandlers(functions);
54 }
55
56private:
57 void EnableVrMode(Kernel::HLERequestContext& ctx) {
58 IPC::ResponseBuilder rb{ctx, 2};
59 rb.Push(RESULT_SUCCESS);
60
61 vr_mode_enabled = true;
62
63 LOG_DEBUG(Service_LBL, "called");
64 }
65
66 void DisableVrMode(Kernel::HLERequestContext& ctx) {
67 IPC::ResponseBuilder rb{ctx, 2};
68 rb.Push(RESULT_SUCCESS);
69
70 vr_mode_enabled = false;
71
72 LOG_DEBUG(Service_LBL, "called");
73 }
74
75 void GetVrMode(Kernel::HLERequestContext& ctx) {
76 IPC::ResponseBuilder rb{ctx, 3};
77 rb.Push(RESULT_SUCCESS);
78 rb.Push(vr_mode_enabled);
79
80 LOG_DEBUG(Service_LBL, "called");
81 }
82
83 bool vr_mode_enabled = false;
84};
85
86void InstallInterfaces(SM::ServiceManager& sm) {
87 std::make_shared<LBL>()->InstallAsService(sm);
88}
89
90} // namespace Service::LBL
diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h
new file mode 100644
index 000000000..bf6f400f8
--- /dev/null
+++ b/src/core/hle/service/lbl/lbl.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::LBL {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::LBL
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index b497376d7..2e99ddf51 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -13,11 +13,11 @@
13 13
14namespace Service::LM { 14namespace Service::LM {
15 15
16class Logger final : public ServiceFramework<Logger> { 16class ILogger final : public ServiceFramework<ILogger> {
17public: 17public:
18 Logger() : ServiceFramework("Logger") { 18 ILogger() : ServiceFramework("ILogger") {
19 static const FunctionInfo functions[] = { 19 static const FunctionInfo functions[] = {
20 {0x00000000, &Logger::Initialize, "Initialize"}, 20 {0x00000000, &ILogger::Initialize, "Initialize"},
21 {0x00000001, nullptr, "SetDestination"}, 21 {0x00000001, nullptr, "SetDestination"},
22 }; 22 };
23 RegisterHandlers(functions); 23 RegisterHandlers(functions);
@@ -182,7 +182,7 @@ public:
182 void OpenLogger(Kernel::HLERequestContext& ctx) { 182 void OpenLogger(Kernel::HLERequestContext& ctx) {
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(RESULT_SUCCESS); 184 rb.Push(RESULT_SUCCESS);
185 rb.PushIpcInterface<Logger>(); 185 rb.PushIpcInterface<ILogger>();
186 186
187 LOG_DEBUG(Service_LM, "called"); 187 LOG_DEBUG(Service_LM, "called");
188 } 188 }
diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp
new file mode 100644
index 000000000..d16367f2c
--- /dev/null
+++ b/src/core/hle/service/mig/mig.cpp
@@ -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#include <memory>
6
7#include "core/hle/service/mig/mig.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::Migration {
12
13class MIG_USR final : public ServiceFramework<MIG_USR> {
14public:
15 explicit MIG_USR() : ServiceFramework{"mig:usr"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {10, nullptr, "TryGetLastMigrationInfo"},
19 {100, nullptr, "CreateServer"},
20 {101, nullptr, "ResumeServer"},
21 {200, nullptr, "CreateClient"},
22 {201, nullptr, "ResumeClient"},
23 };
24 // clang-format on
25
26 RegisterHandlers(functions);
27 }
28};
29
30void InstallInterfaces(SM::ServiceManager& sm) {
31 std::make_shared<MIG_USR>()->InstallAsService(sm);
32}
33
34} // namespace Service::Migration
diff --git a/src/core/hle/service/mig/mig.h b/src/core/hle/service/mig/mig.h
new file mode 100644
index 000000000..288c1c1b3
--- /dev/null
+++ b/src/core/hle/service/mig/mig.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::Migration {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::Migration
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
new file mode 100644
index 000000000..a6197124a
--- /dev/null
+++ b/src/core/hle/service/mii/mii.cpp
@@ -0,0 +1,107 @@
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/kernel/hle_ipc.h"
10#include "core/hle/service/mii/mii.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::Mii {
15
16class IDatabaseService final : public ServiceFramework<IDatabaseService> {
17public:
18 explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "IsUpdated"},
22 {1, nullptr, "IsFullDatabase"},
23 {2, nullptr, "GetCount"},
24 {3, nullptr, "Get"},
25 {4, nullptr, "Get1"},
26 {5, nullptr, "UpdateLatest"},
27 {6, nullptr, "BuildRandom"},
28 {7, nullptr, "BuildDefault"},
29 {8, nullptr, "Get2"},
30 {9, nullptr, "Get3"},
31 {10, nullptr, "UpdateLatest1"},
32 {11, nullptr, "FindIndex"},
33 {12, nullptr, "Move"},
34 {13, nullptr, "AddOrReplace"},
35 {14, nullptr, "Delete"},
36 {15, nullptr, "DestroyFile"},
37 {16, nullptr, "DeleteFile"},
38 {17, nullptr, "Format"},
39 {18, nullptr, "Import"},
40 {19, nullptr, "Export"},
41 {20, nullptr, "IsBrokenDatabaseWithClearFlag"},
42 {21, nullptr, "GetIndex"},
43 {22, nullptr, "SetInterfaceVersion"},
44 {23, nullptr, "Convert"},
45 };
46 // clang-format on
47
48 RegisterHandlers(functions);
49 }
50};
51
52class MiiDBModule final : public ServiceFramework<MiiDBModule> {
53public:
54 explicit MiiDBModule(const char* name) : ServiceFramework{name} {
55 // clang-format off
56 static const FunctionInfo functions[] = {
57 {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
58 };
59 // clang-format on
60
61 RegisterHandlers(functions);
62 }
63
64private:
65 void GetDatabaseService(Kernel::HLERequestContext& ctx) {
66 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
67 rb.Push(RESULT_SUCCESS);
68 rb.PushIpcInterface<IDatabaseService>();
69
70 LOG_DEBUG(Service_Mii, "called");
71 }
72};
73
74class MiiImg final : public ServiceFramework<MiiImg> {
75public:
76 explicit MiiImg() : ServiceFramework{"miiimg"} {
77 // clang-format off
78 static const FunctionInfo functions[] = {
79 {0, nullptr, "Initialize"},
80 {10, nullptr, "Reload"},
81 {11, nullptr, "GetCount"},
82 {12, nullptr, "IsEmpty"},
83 {13, nullptr, "IsFull"},
84 {14, nullptr, "GetAttribute"},
85 {15, nullptr, "LoadImage"},
86 {16, nullptr, "AddOrUpdateImage"},
87 {17, nullptr, "DeleteImages"},
88 {100, nullptr, "DeleteFile"},
89 {101, nullptr, "DestroyFile"},
90 {102, nullptr, "ImportFile"},
91 {103, nullptr, "ExportFile"},
92 {104, nullptr, "ForceInitialize"},
93 };
94 // clang-format on
95
96 RegisterHandlers(functions);
97 }
98};
99
100void InstallInterfaces(SM::ServiceManager& sm) {
101 std::make_shared<MiiDBModule>("mii:e")->InstallAsService(sm);
102 std::make_shared<MiiDBModule>("mii:u")->InstallAsService(sm);
103
104 std::make_shared<MiiImg>()->InstallAsService(sm);
105}
106
107} // namespace Service::Mii
diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h
new file mode 100644
index 000000000..7ce9be50e
--- /dev/null
+++ b/src/core/hle/service/mii/mii.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::Mii {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::Mii
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp
new file mode 100644
index 000000000..0297edca0
--- /dev/null
+++ b/src/core/hle/service/ncm/ncm.cpp
@@ -0,0 +1,59 @@
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/ncm/ncm.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::NCM {
12
13class LocationResolver final : public ServiceFramework<LocationResolver> {
14public:
15 explicit LocationResolver() : ServiceFramework{"lr"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "OpenLocationResolver"},
19 {1, nullptr, "OpenRegisteredLocationResolver"},
20 {2, nullptr, "RefreshLocationResolver"},
21 {3, nullptr, "OpenAddOnContentLocationResolver"},
22 };
23 // clang-format on
24
25 RegisterHandlers(functions);
26 }
27};
28
29class NCM final : public ServiceFramework<NCM> {
30public:
31 explicit NCM() : ServiceFramework{"ncm"} {
32 // clang-format off
33 static const FunctionInfo functions[] = {
34 {0, nullptr, "CreateContentStorage"},
35 {1, nullptr, "CreateContentMetaDatabase"},
36 {2, nullptr, "VerifyContentStorage"},
37 {3, nullptr, "VerifyContentMetaDatabase"},
38 {4, nullptr, "OpenContentStorage"},
39 {5, nullptr, "OpenContentMetaDatabase"},
40 {6, nullptr, "CloseContentStorageForcibly"},
41 {7, nullptr, "CloseContentMetaDatabaseForcibly"},
42 {8, nullptr, "CleanupContentMetaDatabase"},
43 {9, nullptr, "OpenContentStorage2"},
44 {10, nullptr, "CloseContentStorage"},
45 {11, nullptr, "OpenContentMetaDatabase2"},
46 {12, nullptr, "CloseContentMetaDatabase"},
47 };
48 // clang-format on
49
50 RegisterHandlers(functions);
51 }
52};
53
54void InstallInterfaces(SM::ServiceManager& sm) {
55 std::make_shared<LocationResolver>()->InstallAsService(sm);
56 std::make_shared<NCM>()->InstallAsService(sm);
57}
58
59} // namespace Service::NCM
diff --git a/src/core/hle/service/ncm/ncm.h b/src/core/hle/service/ncm/ncm.h
new file mode 100644
index 000000000..7bc8518a6
--- /dev/null
+++ b/src/core/hle/service/ncm/ncm.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::NCM {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::NCM
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
new file mode 100644
index 000000000..8fec97db8
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -0,0 +1,222 @@
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/kernel/hle_ipc.h"
10#include "core/hle/service/nfc/nfc.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::NFC {
15
16class IAm final : public ServiceFramework<IAm> {
17public:
18 explicit IAm() : ServiceFramework{"IAm"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "Initialize"},
22 {1, nullptr, "Finalize"},
23 {2, nullptr, "NotifyForegroundApplet"},
24 };
25 // clang-format on
26
27 RegisterHandlers(functions);
28 }
29};
30
31class NFC_AM final : public ServiceFramework<NFC_AM> {
32public:
33 explicit NFC_AM() : ServiceFramework{"nfc:am"} {
34 // clang-format off
35 static const FunctionInfo functions[] = {
36 {0, &NFC_AM::CreateAmInterface, "CreateAmInterface"},
37 };
38 // clang-format on
39
40 RegisterHandlers(functions);
41 }
42
43private:
44 void CreateAmInterface(Kernel::HLERequestContext& ctx) {
45 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
46 rb.Push(RESULT_SUCCESS);
47 rb.PushIpcInterface<IAm>();
48
49 LOG_DEBUG(Service_NFC, "called");
50 }
51};
52
53class MFIUser final : public ServiceFramework<MFIUser> {
54public:
55 explicit MFIUser() : ServiceFramework{"IUser"} {
56 // clang-format off
57 static const FunctionInfo functions[] = {
58 {0, nullptr, "Initialize"},
59 {1, nullptr, "Finalize"},
60 {2, nullptr, "ListDevices"},
61 {3, nullptr, "StartDetection"},
62 {4, nullptr, "StopDetection"},
63 {5, nullptr, "Read"},
64 {6, nullptr, "Write"},
65 {7, nullptr, "GetTagInfo"},
66 {8, nullptr, "GetActivateEventHandle"},
67 {9, nullptr, "GetDeactivateEventHandle"},
68 {10, nullptr, "GetState"},
69 {11, nullptr, "GetDeviceState"},
70 {12, nullptr, "GetNpadId"},
71 {13, nullptr, "GetAvailabilityChangeEventHandle"},
72 };
73 // clang-format on
74
75 RegisterHandlers(functions);
76 }
77};
78
79class NFC_MF_U final : public ServiceFramework<NFC_MF_U> {
80public:
81 explicit NFC_MF_U() : ServiceFramework{"nfc:mf:u"} {
82 // clang-format off
83 static const FunctionInfo functions[] = {
84 {0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"},
85 };
86 // clang-format on
87
88 RegisterHandlers(functions);
89 }
90
91private:
92 void CreateUserInterface(Kernel::HLERequestContext& ctx) {
93 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
94 rb.Push(RESULT_SUCCESS);
95 rb.PushIpcInterface<MFIUser>();
96
97 LOG_DEBUG(Service_NFC, "called");
98 }
99};
100
101class IUser final : public ServiceFramework<IUser> {
102public:
103 explicit IUser() : ServiceFramework{"IUser"} {
104 // clang-format off
105 static const FunctionInfo functions[] = {
106 {0, nullptr, "Initialize"},
107 {1, nullptr, "Finalize"},
108 {2, nullptr, "GetState"},
109 {3, nullptr, "IsNfcEnabled"},
110 {400, nullptr, "Initialize"},
111 {401, nullptr, "Finalize"},
112 {402, nullptr, "GetState"},
113 {403, nullptr, "IsNfcEnabled"},
114 {404, nullptr, "ListDevices"},
115 {405, nullptr, "GetDeviceState"},
116 {406, nullptr, "GetNpadId"},
117 {407, nullptr, "AttachAvailabilityChangeEvent"},
118 {408, nullptr, "StartDetection"},
119 {409, nullptr, "StopDetection"},
120 {410, nullptr, "GetTagInfo"},
121 {411, nullptr, "AttachActivateEvent"},
122 {412, nullptr, "AttachDeactivateEvent"},
123 {1000, nullptr, "ReadMifare"},
124 {1001, nullptr, "WriteMifare"},
125 {1300, nullptr, "SendCommandByPassThrough"},
126 {1301, nullptr, "KeepPassThroughSession"},
127 {1302, nullptr, "ReleasePassThroughSession"},
128 };
129 // clang-format on
130
131 RegisterHandlers(functions);
132 }
133};
134
135class NFC_U final : public ServiceFramework<NFC_U> {
136public:
137 explicit NFC_U() : ServiceFramework{"nfc:u"} {
138 // clang-format off
139 static const FunctionInfo functions[] = {
140 {0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
141 };
142 // clang-format on
143
144 RegisterHandlers(functions);
145 }
146
147private:
148 void CreateUserInterface(Kernel::HLERequestContext& ctx) {
149 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
150 rb.Push(RESULT_SUCCESS);
151 rb.PushIpcInterface<IUser>();
152
153 LOG_DEBUG(Service_NFC, "called");
154 }
155};
156
157class ISystem final : public ServiceFramework<ISystem> {
158public:
159 explicit ISystem() : ServiceFramework{"ISystem"} {
160 // clang-format off
161 static const FunctionInfo functions[] = {
162 {0, nullptr, "Initialize"},
163 {1, nullptr, "Finalize"},
164 {2, nullptr, "GetState"},
165 {3, nullptr, "IsNfcEnabled"},
166 {100, nullptr, "SetNfcEnabled"},
167 {400, nullptr, "InitializeSystem"},
168 {401, nullptr, "FinalizeSystem"},
169 {402, nullptr, "GetState"},
170 {403, nullptr, "IsNfcEnabled"},
171 {404, nullptr, "ListDevices"},
172 {405, nullptr, "GetDeviceState"},
173 {406, nullptr, "GetNpadId"},
174 {407, nullptr, "AttachAvailabilityChangeEvent"},
175 {408, nullptr, "StartDetection"},
176 {409, nullptr, "StopDetection"},
177 {410, nullptr, "GetTagInfo"},
178 {411, nullptr, "AttachActivateEvent"},
179 {412, nullptr, "AttachDeactivateEvent"},
180 {500, nullptr, "SetNfcEnabled"},
181 {1000, nullptr, "ReadMifare"},
182 {1001, nullptr, "WriteMifare"},
183 {1300, nullptr, "SendCommandByPassThrough"},
184 {1301, nullptr, "KeepPassThroughSession"},
185 {1302, nullptr, "ReleasePassThroughSession"},
186 };
187 // clang-format on
188
189 RegisterHandlers(functions);
190 }
191};
192
193class NFC_SYS final : public ServiceFramework<NFC_SYS> {
194public:
195 explicit NFC_SYS() : ServiceFramework{"nfc:sys"} {
196 // clang-format off
197 static const FunctionInfo functions[] = {
198 {0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"},
199 };
200 // clang-format on
201
202 RegisterHandlers(functions);
203 }
204
205private:
206 void CreateSystemInterface(Kernel::HLERequestContext& ctx) {
207 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
208 rb.Push(RESULT_SUCCESS);
209 rb.PushIpcInterface<ISystem>();
210
211 LOG_DEBUG(Service_NFC, "called");
212 }
213};
214
215void InstallInterfaces(SM::ServiceManager& sm) {
216 std::make_shared<NFC_AM>()->InstallAsService(sm);
217 std::make_shared<NFC_MF_U>()->InstallAsService(sm);
218 std::make_shared<NFC_U>()->InstallAsService(sm);
219 std::make_shared<NFC_SYS>()->InstallAsService(sm);
220}
221
222} // namespace Service::NFC
diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h
new file mode 100644
index 000000000..4d2d815f9
--- /dev/null
+++ b/src/core/hle/service/nfc/nfc.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::NFC {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::NFC
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 89c703310..98017267c 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -2,12 +2,459 @@
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 "core/hle/ipc_helpers.h"
6#include "core/hle/kernel/hle_ipc.h"
5#include "core/hle/service/ns/ns.h" 7#include "core/hle/service/ns/ns.h"
6#include "core/hle/service/ns/pl_u.h" 8#include "core/hle/service/ns/pl_u.h"
7 9
8namespace Service::NS { 10namespace Service::NS {
9 11
12class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
13public:
14 explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, nullptr, "CreateUserAccount"},
18 };
19 // clang-format on
20
21 RegisterHandlers(functions);
22 }
23};
24
25class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
26public:
27 explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} {
28 // clang-format off
29 static const FunctionInfo functions[] = {
30 {0, nullptr, "ListApplicationRecord"},
31 {1, nullptr, "GenerateApplicationRecordCount"},
32 {2, nullptr, "GetApplicationRecordUpdateSystemEvent"},
33 {3, nullptr, "GetApplicationViewDeprecated"},
34 {4, nullptr, "DeleteApplicationEntity"},
35 {5, nullptr, "DeleteApplicationCompletely"},
36 {6, nullptr, "IsAnyApplicationEntityRedundant"},
37 {7, nullptr, "DeleteRedundantApplicationEntity"},
38 {8, nullptr, "IsApplicationEntityMovable"},
39 {9, nullptr, "MoveApplicationEntity"},
40 {11, nullptr, "CalculateApplicationOccupiedSize"},
41 {16, nullptr, "PushApplicationRecord"},
42 {17, nullptr, "ListApplicationRecordContentMeta"},
43 {19, nullptr, "LaunchApplication"},
44 {21, nullptr, "GetApplicationContentPath"},
45 {22, nullptr, "TerminateApplication"},
46 {23, nullptr, "ResolveApplicationContentPath"},
47 {26, nullptr, "BeginInstallApplication"},
48 {27, nullptr, "DeleteApplicationRecord"},
49 {30, nullptr, "RequestApplicationUpdateInfo"},
50 {32, nullptr, "CancelApplicationDownload"},
51 {33, nullptr, "ResumeApplicationDownload"},
52 {35, nullptr, "UpdateVersionList"},
53 {36, nullptr, "PushLaunchVersion"},
54 {37, nullptr, "ListRequiredVersion"},
55 {38, nullptr, "CheckApplicationLaunchVersion"},
56 {39, nullptr, "CheckApplicationLaunchRights"},
57 {40, nullptr, "GetApplicationLogoData"},
58 {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
59 {42, nullptr, "CleanupSdCard"},
60 {43, nullptr, "CheckSdCardMountStatus"},
61 {44, nullptr, "GetSdCardMountStatusChangedEvent"},
62 {45, nullptr, "GetGameCardAttachmentEvent"},
63 {46, nullptr, "GetGameCardAttachmentInfo"},
64 {47, nullptr, "GetTotalSpaceSize"},
65 {48, nullptr, "GetFreeSpaceSize"},
66 {49, nullptr, "GetSdCardRemovedEvent"},
67 {52, nullptr, "GetGameCardUpdateDetectionEvent"},
68 {53, nullptr, "DisableApplicationAutoDelete"},
69 {54, nullptr, "EnableApplicationAutoDelete"},
70 {55, nullptr, "GetApplicationDesiredLanguage"},
71 {56, nullptr, "SetApplicationTerminateResult"},
72 {57, nullptr, "ClearApplicationTerminateResult"},
73 {58, nullptr, "GetLastSdCardMountUnexpectedResult"},
74 {59, nullptr, "ConvertApplicationLanguageToLanguageCode"},
75 {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
76 {61, nullptr, "GetBackgroundDownloadStressTaskInfo"},
77 {62, nullptr, "GetGameCardStopper"},
78 {63, nullptr, "IsSystemProgramInstalled"},
79 {64, nullptr, "StartApplyDeltaTask"},
80 {65, nullptr, "GetRequestServerStopper"},
81 {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"},
82 {67, nullptr, "CancelApplicationApplyDelta"},
83 {68, nullptr, "ResumeApplicationApplyDelta"},
84 {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"},
85 {70, nullptr, "ResumeAll"},
86 {71, nullptr, "GetStorageSize"},
87 {80, nullptr, "RequestDownloadApplication"},
88 {81, nullptr, "RequestDownloadAddOnContent"},
89 {82, nullptr, "DownloadApplication"},
90 {83, nullptr, "CheckApplicationResumeRights"},
91 {84, nullptr, "GetDynamicCommitEvent"},
92 {85, nullptr, "RequestUpdateApplication2"},
93 {86, nullptr, "EnableApplicationCrashReport"},
94 {87, nullptr, "IsApplicationCrashReportEnabled"},
95 {90, nullptr, "BoostSystemMemoryResourceLimit"},
96 {100, nullptr, "ResetToFactorySettings"},
97 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
98 {102, nullptr, "ResetToFactorySettingsForRefurbishment"},
99 {200, nullptr, "CalculateUserSaveDataStatistics"},
100 {201, nullptr, "DeleteUserSaveDataAll"},
101 {210, nullptr, "DeleteUserSystemSaveData"},
102 {220, nullptr, "UnregisterNetworkServiceAccount"},
103 {300, nullptr, "GetApplicationShellEvent"},
104 {301, nullptr, "PopApplicationShellEventInfo"},
105 {302, nullptr, "LaunchLibraryApplet"},
106 {303, nullptr, "TerminateLibraryApplet"},
107 {304, nullptr, "LaunchSystemApplet"},
108 {305, nullptr, "TerminateSystemApplet"},
109 {306, nullptr, "LaunchOverlayApplet"},
110 {307, nullptr, "TerminateOverlayApplet"},
111 {400, nullptr, "GetApplicationControlData"},
112 {401, nullptr, "InvalidateAllApplicationControlCache"},
113 {402, nullptr, "RequestDownloadApplicationControlData"},
114 {403, nullptr, "GetMaxApplicationControlCacheCount"},
115 {404, nullptr, "InvalidateApplicationControlCache"},
116 {405, nullptr, "ListApplicationControlCacheEntryInfo"},
117 {502, nullptr, "RequestCheckGameCardRegistration"},
118 {503, nullptr, "RequestGameCardRegistrationGoldPoint"},
119 {504, nullptr, "RequestRegisterGameCard"},
120 {505, nullptr, "GetGameCardMountFailureEvent"},
121 {506, nullptr, "IsGameCardInserted"},
122 {507, nullptr, "EnsureGameCardAccess"},
123 {508, nullptr, "GetLastGameCardMountFailureResult"},
124 {509, nullptr, "ListApplicationIdOnGameCard"},
125 {600, nullptr, "CountApplicationContentMeta"},
126 {601, nullptr, "ListApplicationContentMetaStatus"},
127 {602, nullptr, "ListAvailableAddOnContent"},
128 {603, nullptr, "GetOwnedApplicationContentMetaStatus"},
129 {604, nullptr, "RegisterContentsExternalKey"},
130 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
131 {606, nullptr, "GetContentMetaStorage"},
132 {700, nullptr, "PushDownloadTaskList"},
133 {701, nullptr, "ClearTaskStatusList"},
134 {702, nullptr, "RequestDownloadTaskList"},
135 {703, nullptr, "RequestEnsureDownloadTask"},
136 {704, nullptr, "ListDownloadTaskStatus"},
137 {705, nullptr, "RequestDownloadTaskListData"},
138 {800, nullptr, "RequestVersionList"},
139 {801, nullptr, "ListVersionList"},
140 {802, nullptr, "RequestVersionListData"},
141 {900, nullptr, "GetApplicationRecord"},
142 {901, nullptr, "GetApplicationRecordProperty"},
143 {902, nullptr, "EnableApplicationAutoUpdate"},
144 {903, nullptr, "DisableApplicationAutoUpdate"},
145 {904, nullptr, "TouchApplication"},
146 {905, nullptr, "RequestApplicationUpdate"},
147 {906, nullptr, "IsApplicationUpdateRequested"},
148 {907, nullptr, "WithdrawApplicationUpdateRequest"},
149 {908, nullptr, "ListApplicationRecordInstalledContentMeta"},
150 {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"},
151 {1000, nullptr, "RequestVerifyApplicationDeprecated"},
152 {1001, nullptr, "CorruptApplicationForDebug"},
153 {1002, nullptr, "RequestVerifyAddOnContentsRights"},
154 {1003, nullptr, "RequestVerifyApplication"},
155 {1004, nullptr, "CorruptContentForDebug"},
156 {1200, nullptr, "NeedsUpdateVulnerability"},
157 {1300, nullptr, "IsAnyApplicationEntityInstalled"},
158 {1301, nullptr, "DeleteApplicationContentEntities"},
159 {1302, nullptr, "CleanupUnrecordedApplicationEntity"},
160 {1303, nullptr, "CleanupAddOnContentsWithNoRights"},
161 {1304, nullptr, "DeleteApplicationContentEntity"},
162 {1305, nullptr, "TryDeleteRunningApplicationEntity"},
163 {1306, nullptr, "TryDeleteRunningApplicationCompletely"},
164 {1307, nullptr, "TryDeleteRunningApplicationContentEntities"},
165 {1400, nullptr, "PrepareShutdown"},
166 {1500, nullptr, "FormatSdCard"},
167 {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"},
168 {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"},
169 {1504, nullptr, "InsertSdCard"},
170 {1505, nullptr, "RemoveSdCard"},
171 {1600, nullptr, "GetSystemSeedForPseudoDeviceId"},
172 {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"},
173 {1700, nullptr, "ListApplicationDownloadingContentMeta"},
174 {1701, nullptr, "GetApplicationView"},
175 {1702, nullptr, "GetApplicationDownloadTaskStatus"},
176 {1703, nullptr, "GetApplicationViewDownloadErrorContext"},
177 {1800, nullptr, "IsNotificationSetupCompleted"},
178 {1801, nullptr, "GetLastNotificationInfoCount"},
179 {1802, nullptr, "ListLastNotificationInfo"},
180 {1803, nullptr, "ListNotificationTask"},
181 {1900, nullptr, "IsActiveAccount"},
182 {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"},
183 {1902, nullptr, "GetApplicationTicketInfo"},
184 {2000, nullptr, "GetSystemDeliveryInfo"},
185 {2001, nullptr, "SelectLatestSystemDeliveryInfo"},
186 {2002, nullptr, "VerifyDeliveryProtocolVersion"},
187 {2003, nullptr, "GetApplicationDeliveryInfo"},
188 {2004, nullptr, "HasAllContentsToDeliver"},
189 {2005, nullptr, "CompareApplicationDeliveryInfo"},
190 {2006, nullptr, "CanDeliverApplication"},
191 {2007, nullptr, "ListContentMetaKeyToDeliverApplication"},
192 {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"},
193 {2009, nullptr, "EstimateRequiredSize"},
194 {2010, nullptr, "RequestReceiveApplication"},
195 {2011, nullptr, "CommitReceiveApplication"},
196 {2012, nullptr, "GetReceiveApplicationProgress"},
197 {2013, nullptr, "RequestSendApplication"},
198 {2014, nullptr, "GetSendApplicationProgress"},
199 {2015, nullptr, "CompareSystemDeliveryInfo"},
200 {2016, nullptr, "ListNotCommittedContentMeta"},
201 {2017, nullptr, "CreateDownloadTask"},
202 };
203 // clang-format on
204
205 RegisterHandlers(functions);
206 }
207};
208
209class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
210public:
211 explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} {
212 // clang-format off
213 static const FunctionInfo functions[] = {
214 {0, nullptr, "GetLaunchRequiredVersion"},
215 {1, nullptr, "UpgradeLaunchRequiredVersion"},
216 {35, nullptr, "UpdateVersionList"},
217 {36, nullptr, "PushLaunchVersion"},
218 {37, nullptr, "ListRequiredVersion"},
219 {800, nullptr, "RequestVersionList"},
220 {801, nullptr, "ListVersionList"},
221 {802, nullptr, "RequestVersionListData"},
222 {1000, nullptr, "PerformAutoUpdate"},
223 };
224 // clang-format on
225
226 RegisterHandlers(functions);
227 }
228};
229
230class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
231public:
232 explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} {
233 // clang-format off
234 static const FunctionInfo functions[] = {
235 {11, nullptr, "CalculateApplicationOccupiedSize"},
236 {43, nullptr, "CheckSdCardMountStatus"},
237 {47, nullptr, "GetTotalSpaceSize"},
238 {48, nullptr, "GetFreeSpaceSize"},
239 {600, nullptr, "CountApplicationContentMeta"},
240 {601, nullptr, "ListApplicationContentMetaStatus"},
241 {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
242 {607, nullptr, "IsAnyApplicationRunning"},
243 };
244 // clang-format on
245
246 RegisterHandlers(functions);
247 }
248};
249
250class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
251public:
252 explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} {
253 // clang-format off
254 static const FunctionInfo functions[] = {
255 {21, nullptr, "GetApplicationContentPath"},
256 {23, nullptr, "ResolveApplicationContentPath"},
257 };
258 // clang-format on
259
260 RegisterHandlers(functions);
261 }
262};
263
264class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
265public:
266 explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} {
267 // clang-format off
268 static const FunctionInfo functions[] = {
269 {701, nullptr, "ClearTaskStatusList"},
270 {702, nullptr, "RequestDownloadTaskList"},
271 {703, nullptr, "RequestEnsureDownloadTask"},
272 {704, nullptr, "ListDownloadTaskStatus"},
273 {705, nullptr, "RequestDownloadTaskListData"},
274 {706, nullptr, "TryCommitCurrentApplicationDownloadTask"},
275 {707, nullptr, "EnableAutoCommit"},
276 {708, nullptr, "DisableAutoCommit"},
277 {709, nullptr, "TriggerDynamicCommitEvent"},
278 };
279 // clang-format on
280
281 RegisterHandlers(functions);
282 }
283};
284
285class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
286public:
287 explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} {
288 // clang-format off
289 static const FunctionInfo functions[] = {
290 {0, nullptr, "RequestLinkDevice"},
291 };
292 // clang-format on
293
294 RegisterHandlers(functions);
295 }
296};
297
298class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
299public:
300 explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} {
301 // clang-format off
302 static const FunctionInfo functions[] = {
303 {100, nullptr, "ResetToFactorySettings"},
304 {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"},
305 {102, nullptr, "ResetToFactorySettingsForRefurbishment "},
306 };
307 // clang-format on
308
309 RegisterHandlers(functions);
310 }
311};
312
313class NS final : public ServiceFramework<NS> {
314public:
315 explicit NS(const char* name) : ServiceFramework{name} {
316 // clang-format off
317 static const FunctionInfo functions[] = {
318 {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
319 {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
320 {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
321 {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
322 {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
323 {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
324 {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"},
325 {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
326 };
327 // clang-format on
328
329 RegisterHandlers(functions);
330 }
331
332private:
333 template <typename T>
334 void PushInterface(Kernel::HLERequestContext& ctx) {
335 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
336 rb.Push(RESULT_SUCCESS);
337 rb.PushIpcInterface<T>();
338
339 LOG_DEBUG(Service_NS, "called");
340 }
341};
342
343class NS_DEV final : public ServiceFramework<NS_DEV> {
344public:
345 explicit NS_DEV() : ServiceFramework{"ns:dev"} {
346 // clang-format off
347 static const FunctionInfo functions[] = {
348 {0, nullptr, "LaunchProgram"},
349 {1, nullptr, "TerminateProcess"},
350 {2, nullptr, "TerminateProgram"},
351 {3, nullptr, "GetShellEventHandle"},
352 {4, nullptr, "GetShellEventInfo"},
353 {5, nullptr, "TerminateApplication"},
354 {6, nullptr, "PrepareLaunchProgramFromHost"},
355 {7, nullptr, "LaunchApplication"},
356 {8, nullptr, "LaunchApplicationWithStorageId"},
357 };
358 // clang-format on
359
360 RegisterHandlers(functions);
361 }
362};
363
364class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> {
365public:
366 explicit ISystemUpdateControl() : ServiceFramework{"ISystemUpdateControl"} {
367 // clang-format off
368 static const FunctionInfo functions[] = {
369 {0, nullptr, "HasDownloaded"},
370 {1, nullptr, "RequestCheckLatestUpdate"},
371 {2, nullptr, "RequestDownloadLatestUpdate"},
372 {3, nullptr, "GetDownloadProgress"},
373 {4, nullptr, "ApplyDownloadedUpdate"},
374 {5, nullptr, "RequestPrepareCardUpdate"},
375 {6, nullptr, "GetPrepareCardUpdateProgress"},
376 {7, nullptr, "HasPreparedCardUpdate"},
377 {8, nullptr, "ApplyCardUpdate"},
378 {9, nullptr, "GetDownloadedEulaDataSize"},
379 {10, nullptr, "GetDownloadedEulaData"},
380 {11, nullptr, "SetupCardUpdate"},
381 {12, nullptr, "GetPreparedCardUpdateEulaDataSize"},
382 {13, nullptr, "GetPreparedCardUpdateEulaData"},
383 {14, nullptr, "SetupCardUpdateViaSystemUpdater"},
384 {15, nullptr, "HasReceived"},
385 {16, nullptr, "RequestReceiveSystemUpdate"},
386 {17, nullptr, "GetReceiveProgress"},
387 {18, nullptr, "ApplyReceivedUpdate"},
388 {19, nullptr, "GetReceivedEulaDataSize"},
389 {20, nullptr, "GetReceivedEulaData"},
390 {21, nullptr, "SetupToReceiveSystemUpdate"},
391 };
392 // clang-format on
393
394 RegisterHandlers(functions);
395 }
396};
397
398class NS_SU final : public ServiceFramework<NS_SU> {
399public:
400 explicit NS_SU() : ServiceFramework{"ns:su"} {
401 // clang-format off
402 static const FunctionInfo functions[] = {
403 {0, nullptr, "GetBackgroundNetworkUpdateState"},
404 {1, &NS_SU::OpenSystemUpdateControl, "OpenSystemUpdateControl"},
405 {2, nullptr, "NotifyExFatDriverRequired"},
406 {3, nullptr, "ClearExFatDriverStatusForDebug"},
407 {4, nullptr, "RequestBackgroundNetworkUpdate"},
408 {5, nullptr, "NotifyBackgroundNetworkUpdate"},
409 {6, nullptr, "NotifyExFatDriverDownloadedForDebug"},
410 {9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"},
411 {10, nullptr, "NotifySystemUpdateForContentDelivery"},
412 {11, nullptr, "PrepareShutdown"},
413 {16, nullptr, "DestroySystemUpdateTask"},
414 {17, nullptr, "RequestSendSystemUpdate"},
415 {18, nullptr, "GetSendSystemUpdateProgress"},
416 };
417 // clang-format on
418
419 RegisterHandlers(functions);
420 }
421
422private:
423 void OpenSystemUpdateControl(Kernel::HLERequestContext& ctx) {
424 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
425 rb.Push(RESULT_SUCCESS);
426 rb.PushIpcInterface<ISystemUpdateControl>();
427
428 LOG_DEBUG(Service_NS, "called");
429 }
430};
431
432class NS_VM final : public ServiceFramework<NS_VM> {
433public:
434 explicit NS_VM() : ServiceFramework{"ns:vm"} {
435 // clang-format off
436 static const FunctionInfo functions[] = {
437 {1200, nullptr, "NeedsUpdateVulnerability"},
438 {1201, nullptr, "UpdateSafeSystemVersionForDebug"},
439 {1202, nullptr, "GetSafeSystemVersion"},
440 };
441 // clang-format on
442
443 RegisterHandlers(functions);
444 }
445};
446
10void InstallInterfaces(SM::ServiceManager& service_manager) { 447void InstallInterfaces(SM::ServiceManager& service_manager) {
448 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
449 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
450 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
451 std::make_shared<NS>("ns:rt")->InstallAsService(service_manager);
452 std::make_shared<NS>("ns:web")->InstallAsService(service_manager);
453
454 std::make_shared<NS_DEV>()->InstallAsService(service_manager);
455 std::make_shared<NS_SU>()->InstallAsService(service_manager);
456 std::make_shared<NS_VM>()->InstallAsService(service_manager);
457
11 std::make_shared<PL_U>()->InstallAsService(service_manager); 458 std::make_shared<PL_U>()->InstallAsService(service_manager);
12} 459}
13 460
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index ed69a4325..8bc49935a 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -7,8 +7,8 @@
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 8#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
9#include "core/hle/service/nvdrv/devices/nvmap.h" 9#include "core/hle/service/nvdrv/devices/nvmap.h"
10#include "video_core/gpu.h"
10#include "video_core/renderer_base.h" 11#include "video_core/renderer_base.h"
11#include "video_core/video_core.h"
12 12
13namespace Service::Nvidia::Devices { 13namespace Service::Nvidia::Devices {
14 14
@@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
30 addr, offset, width, height, stride, static_cast<PixelFormat>(format), 30 addr, offset, width, height, stride, static_cast<PixelFormat>(format),
31 transform, crop_rect}; 31 transform, crop_rect};
32 32
33 Core::System::GetInstance().perf_stats.EndGameFrame(); 33 auto& instance = Core::System::GetInstance();
34 34 instance.perf_stats.EndGameFrame();
35 VideoCore::g_renderer->SwapBuffers(framebuffer); 35 instance.Renderer().SwapBuffers(framebuffer);
36} 36}
37 37
38} // namespace Service::Nvidia::Devices 38} // namespace Service::Nvidia::Devices
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 57b128b40..be2b79256 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -2,14 +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 <cinttypes> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
10#include "core/hle/service/nvdrv/devices/nvmap.h" 10#include "core/hle/service/nvdrv/devices/nvmap.h"
11#include "video_core/memory_manager.h"
12#include "video_core/rasterizer_interface.h"
11#include "video_core/renderer_base.h" 13#include "video_core/renderer_base.h"
12#include "video_core/video_core.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
@@ -150,15 +151,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
150 151
151 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); 152 LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
152 153
153 auto& gpu = Core::System::GetInstance().GPU(); 154 const auto itr = buffer_mappings.find(params.offset);
154
155 auto itr = buffer_mappings.find(params.offset);
156
157 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); 155 ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping");
158 156
157 auto& system_instance = Core::System::GetInstance();
158
159 // Remove this memory region from the rasterizer cache. 159 // Remove this memory region from the rasterizer cache.
160 VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size); 160 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset,
161 itr->second.size);
161 162
163 auto& gpu = system_instance.GPU();
162 params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size); 164 params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size);
163 165
164 buffer_mappings.erase(itr->second.offset); 166 buffer_mappings.erase(itr->second.offset);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 671b092e1..5685eb2be 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -2,6 +2,9 @@
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 <cstdlib>
6#include <cstring>
7
5#include "common/assert.h" 8#include "common/assert.h"
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" 10#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 090261a60..6b496e9fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -5,8 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <cstdlib>
9#include <cstring>
10#include <vector> 8#include <vector>
11#include "common/common_types.h" 9#include "common/common_types.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 10#include "core/hle/service/nvdrv/devices/nvdevice.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 44e062f50..ae421247d 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -2,7 +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 <cinttypes> 5#include <cstring>
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" 8#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
@@ -97,7 +97,9 @@ u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>&
97u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { 97u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
98 LOG_DEBUG(Service_NVDRV, "called"); 98 LOG_DEBUG(Service_NVDRV, "called");
99 IoctlActiveSlotMask params{}; 99 IoctlActiveSlotMask params{};
100 std::memcpy(&params, input.data(), input.size()); 100 if (input.size() > 0) {
101 std::memcpy(&params, input.data(), input.size());
102 }
101 params.slot = 0x07; 103 params.slot = 0x07;
102 params.mask = 0x01; 104 params.mask = 0x01;
103 std::memcpy(output.data(), &params, output.size()); 105 std::memcpy(output.data(), &params, output.size());
@@ -107,7 +109,9 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
107u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { 109u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
108 LOG_DEBUG(Service_NVDRV, "called"); 110 LOG_DEBUG(Service_NVDRV, "called");
109 IoctlZcullGetCtxSize params{}; 111 IoctlZcullGetCtxSize params{};
110 std::memcpy(&params, input.data(), input.size()); 112 if (input.size() > 0) {
113 std::memcpy(&params, input.data(), input.size());
114 }
111 params.size = 0x1; 115 params.size = 0x1;
112 std::memcpy(output.data(), &params, output.size()); 116 std::memcpy(output.data(), &params, output.size());
113 return 0; 117 return 0;
@@ -116,7 +120,11 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
116u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { 120u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
117 LOG_DEBUG(Service_NVDRV, "called"); 121 LOG_DEBUG(Service_NVDRV, "called");
118 IoctlNvgpuGpuZcullGetInfoArgs params{}; 122 IoctlNvgpuGpuZcullGetInfoArgs params{};
119 std::memcpy(&params, input.data(), input.size()); 123
124 if (input.size() > 0) {
125 std::memcpy(&params, input.data(), input.size());
126 }
127
120 params.width_align_pixels = 0x20; 128 params.width_align_pixels = 0x20;
121 params.height_align_pixels = 0x20; 129 params.height_align_pixels = 0x20;
122 params.pixel_squares_by_aliquots = 0x400; 130 params.pixel_squares_by_aliquots = 0x400;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 126782573..116dabedb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -2,12 +2,14 @@
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 <cinttypes> 5#include <cstring>
6#include <map>
7#include "common/assert.h" 6#include "common/assert.h"
8#include "common/logging/log.h" 7#include "common/logging/log.h"
9#include "core/core.h" 8#include "core/core.h"
10#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" 9#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
10#include "core/memory.h"
11#include "video_core/gpu.h"
12#include "video_core/memory_manager.h"
11 13
12namespace Service::Nvidia::Devices { 14namespace Service::Nvidia::Devices {
13 15
@@ -132,9 +134,12 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
132 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 134 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
133 params.address, params.num_entries, params.flags); 135 params.address, params.num_entries, params.flags);
134 136
135 auto entries = std::vector<IoctlGpfifoEntry>(); 137 ASSERT_MSG(input.size() ==
136 entries.resize(params.num_entries); 138 sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(IoctlGpfifoEntry),
137 std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)], 139 "Incorrect input size");
140
141 std::vector<IoctlGpfifoEntry> entries(params.num_entries);
142 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
138 params.num_entries * sizeof(IoctlGpfifoEntry)); 143 params.num_entries * sizeof(IoctlGpfifoEntry));
139 for (auto entry : entries) { 144 for (auto entry : entries) {
140 Tegra::GPUVAddr va_addr = entry.Address(); 145 Tegra::GPUVAddr va_addr = entry.Address();
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index aa8df2e6e..650ed8fbc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -6,6 +6,7 @@
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/bit_field.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/swap.h" 11#include "common/swap.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index b51c73ee8..364619e67 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.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 <cstring>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
7#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" 9#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 0192aecdd..6ad74421b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <cstdlib>
9#include <cstring>
10#include <vector> 7#include <vector>
11#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 10#include "core/hle/service/nvdrv/devices/nvdevice.h"
13 11
14namespace Service::Nvidia::Devices { 12namespace Service::Nvidia::Devices {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 724eeb139..e9305bfb3 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.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 <cinttypes> 6#include <cstring>
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 959b5ba29..1c3529bb6 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <string>
9#include "core/hle/kernel/event.h" 8#include "core/hle/kernel/event.h"
10#include "core/hle/service/nvdrv/nvdrv.h" 9#include "core/hle/service/nvdrv/nvdrv.h"
11#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 1555ea806..e8b30921a 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -54,7 +54,7 @@ u32 Module::Open(const std::string& device_name) {
54 return fd; 54 return fd;
55} 55}
56 56
57u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) { 57u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
58 auto itr = open_files.find(fd); 58 auto itr = open_files.find(fd);
59 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 59 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
60 60
diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp
index 9ca6e5512..0e8e21bad 100644
--- a/src/core/hle/service/nvdrv/nvmemp.cpp
+++ b/src/core/hle/service/nvdrv/nvmemp.cpp
@@ -4,8 +4,6 @@
4 4
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/nvdrv/nvdrv.h"
9#include "core/hle/service/nvdrv/nvmemp.h" 7#include "core/hle/service/nvdrv/nvmemp.h"
10 8
11namespace Service::Nvidia { 9namespace Service::Nvidia {
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 7132b18ad..adf180509 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -4,9 +4,8 @@
4 4
5#include <algorithm> 5#include <algorithm>
6 6
7#include "common/alignment.h" 7#include "common/assert.h"
8#include "common/scope_exit.h" 8#include "common/logging/log.h"
9#include "core/core_timing.h"
10#include "core/hle/service/nvflinger/buffer_queue.h" 9#include "core/hle/service/nvflinger/buffer_queue.h"
11 10
12namespace Service { 11namespace Service {
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 5344441e1..570aa8493 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -3,8 +3,11 @@
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 7
7#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/assert.h"
10#include "common/logging/log.h"
8#include "common/microprofile.h" 11#include "common/microprofile.h"
9#include "common/scope_exit.h" 12#include "common/scope_exit.h"
10#include "core/core.h" 13#include "core/core.h"
@@ -31,7 +34,7 @@ NVFlinger::NVFlinger() {
31 34
32 // Schedule the screen composition events 35 // Schedule the screen composition events
33 composition_event = 36 composition_event =
34 CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { 37 CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
35 Compose(); 38 Compose();
36 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); 39 CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
37 }); 40 });
@@ -43,7 +46,7 @@ NVFlinger::~NVFlinger() {
43 CoreTiming::UnscheduleEvent(composition_event, 0); 46 CoreTiming::UnscheduleEvent(composition_event, 0);
44} 47}
45 48
46u64 NVFlinger::OpenDisplay(const std::string& name) { 49u64 NVFlinger::OpenDisplay(std::string_view name) {
47 LOG_WARNING(Service, "Opening display {}", name); 50 LOG_WARNING(Service, "Opening display {}", name);
48 51
49 // TODO(Subv): Currently we only support the Default display. 52 // TODO(Subv): Currently we only support the Default display.
@@ -127,9 +130,11 @@ void NVFlinger::Compose() {
127 MicroProfileFlip(); 130 MicroProfileFlip();
128 131
129 if (buffer == boost::none) { 132 if (buffer == boost::none) {
133 auto& system_instance = Core::System::GetInstance();
134
130 // There was no queued buffer to draw, render previous frame 135 // There was no queued buffer to draw, render previous frame
131 Core::System::GetInstance().perf_stats.EndGameFrame(); 136 system_instance.perf_stats.EndGameFrame();
132 VideoCore::g_renderer->SwapBuffers({}); 137 system_instance.Renderer().SwapBuffers({});
133 continue; 138 continue;
134 } 139 }
135 140
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 2c908297b..5374df175 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -5,7 +5,11 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <boost/optional.hpp> 8#include <string>
9#include <string_view>
10#include <vector>
11
12#include "common/common_types.h"
9#include "core/hle/kernel/event.h" 13#include "core/hle/kernel/event.h"
10 14
11namespace CoreTiming { 15namespace CoreTiming {
@@ -41,7 +45,7 @@ public:
41 ~NVFlinger(); 45 ~NVFlinger();
42 46
43 /// Opens the specified display and returns the id. 47 /// Opens the specified display and returns the id.
44 u64 OpenDisplay(const std::string& name); 48 u64 OpenDisplay(std::string_view name);
45 49
46 /// Creates a layer on the specified display and returns the layer id. 50 /// Creates a layer on the specified display and returns the layer id.
47 u64 CreateLayer(u64 display_id); 51 u64 CreateLayer(u64 display_id);
diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp
new file mode 100644
index 000000000..39cf05eba
--- /dev/null
+++ b/src/core/hle/service/pcie/pcie.cpp
@@ -0,0 +1,64 @@
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/pcie/pcie.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::PCIe {
12
13class ISession final : public ServiceFramework<ISession> {
14public:
15 explicit ISession() : ServiceFramework{"ISession"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "QueryFunctions"},
19 {1, nullptr, "AcquireFunction"},
20 {2, nullptr, "ReleaseFunction"},
21 {3, nullptr, "GetFunctionState"},
22 {4, nullptr, "GetBarProfile"},
23 {5, nullptr, "ReadConfig"},
24 {6, nullptr, "WriteConfig"},
25 {7, nullptr, "ReadBarRegion"},
26 {8, nullptr, "WriteBarRegion"},
27 {9, nullptr, "FindCapability"},
28 {10, nullptr, "FindExtendedCapability"},
29 {11, nullptr, "MapDma"},
30 {12, nullptr, "UnmapDma"},
31 {13, nullptr, "UnmapDmaBusAddress"},
32 {14, nullptr, "GetDmaBusAddress"},
33 {15, nullptr, "GetDmaBusAddressRange"},
34 {16, nullptr, "SetDmaEnable"},
35 {17, nullptr, "AcquireIrq"},
36 {18, nullptr, "ReleaseIrq"},
37 {19, nullptr, "SetIrqEnable"},
38 {20, nullptr, "SetAspmEnable"},
39 };
40 // clang-format on
41
42 RegisterHandlers(functions);
43 }
44};
45
46class PCIe final : public ServiceFramework<PCIe> {
47public:
48 explicit PCIe() : ServiceFramework{"pcie"} {
49 // clang-format off
50 static const FunctionInfo functions[] = {
51 {0, nullptr, "RegisterClassDriver"},
52 {1, nullptr, "QueryFunctionsUnregistered"},
53 };
54 // clang-format on
55
56 RegisterHandlers(functions);
57 }
58};
59
60void InstallInterfaces(SM::ServiceManager& sm) {
61 std::make_shared<PCIe>()->InstallAsService(sm);
62}
63
64} // namespace Service::PCIe
diff --git a/src/core/hle/service/pcie/pcie.h b/src/core/hle/service/pcie/pcie.h
new file mode 100644
index 000000000..59c22ca45
--- /dev/null
+++ b/src/core/hle/service/pcie/pcie.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::PCIe {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::PCIe
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
new file mode 100644
index 000000000..d6891a659
--- /dev/null
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -0,0 +1,84 @@
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/pcv/pcv.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::PCV {
12
13class PCV final : public ServiceFramework<PCV> {
14public:
15 explicit PCV() : ServiceFramework{"pcv"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "SetPowerEnabled"},
19 {1, nullptr, "SetClockEnabled"},
20 {2, nullptr, "SetClockRate"},
21 {3, nullptr, "GetClockRate"},
22 {4, nullptr, "GetState"},
23 {5, nullptr, "GetPossibleClockRates"},
24 {6, nullptr, "SetMinVClockRate"},
25 {7, nullptr, "SetReset"},
26 {8, nullptr, "SetVoltageEnabled"},
27 {9, nullptr, "GetVoltageEnabled"},
28 {10, nullptr, "GetVoltageRange"},
29 {11, nullptr, "SetVoltageValue"},
30 {12, nullptr, "GetVoltageValue"},
31 {13, nullptr, "GetTemperatureThresholds"},
32 {14, nullptr, "SetTemperature"},
33 {15, nullptr, "Initialize"},
34 {16, nullptr, "IsInitialized"},
35 {17, nullptr, "Finalize"},
36 {18, nullptr, "PowerOn"},
37 {19, nullptr, "PowerOff"},
38 {20, nullptr, "ChangeVoltage"},
39 {21, nullptr, "GetPowerClockInfoEvent"},
40 {22, nullptr, "GetOscillatorClock"},
41 {23, nullptr, "GetDvfsTable"},
42 {24, nullptr, "GetModuleStateTable"},
43 {25, nullptr, "GetPowerDomainStateTable"},
44 {26, nullptr, "GetFuseInfo"},
45 };
46 // clang-format on
47
48 RegisterHandlers(functions);
49 }
50};
51
52class PCV_ARB final : public ServiceFramework<PCV_ARB> {
53public:
54 explicit PCV_ARB() : ServiceFramework{"pcv:arb"} {
55 // clang-format off
56 static const FunctionInfo functions[] = {
57 {0, nullptr, "ReleaseControl"},
58 };
59 // clang-format on
60
61 RegisterHandlers(functions);
62 }
63};
64
65class PCV_IMM final : public ServiceFramework<PCV_IMM> {
66public:
67 explicit PCV_IMM() : ServiceFramework{"pcv:imm"} {
68 // clang-format off
69 static const FunctionInfo functions[] = {
70 {0, nullptr, "SetClockRate"},
71 };
72 // clang-format on
73
74 RegisterHandlers(functions);
75 }
76};
77
78void InstallInterfaces(SM::ServiceManager& sm) {
79 std::make_shared<PCV>()->InstallAsService(sm);
80 std::make_shared<PCV_ARB>()->InstallAsService(sm);
81 std::make_shared<PCV_IMM>()->InstallAsService(sm);
82}
83
84} // namespace Service::PCV
diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h
new file mode 100644
index 000000000..219a893c3
--- /dev/null
+++ b/src/core/hle/service/pcv/pcv.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::PCV {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::PCV
diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp
new file mode 100644
index 000000000..bbad870a2
--- /dev/null
+++ b/src/core/hle/service/psc/psc.cpp
@@ -0,0 +1,77 @@
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/psc/psc.h"
10#include "core/hle/service/service.h"
11#include "core/hle/service/sm/sm.h"
12
13namespace Service::PSC {
14
15class PSC_C final : public ServiceFramework<PSC_C> {
16public:
17 explicit PSC_C() : ServiceFramework{"psc:c"} {
18 // clang-format off
19 static const FunctionInfo functions[] = {
20 {0, nullptr, "Unknown1"},
21 {1, nullptr, "Unknown2"},
22 {2, nullptr, "Unknown3"},
23 {3, nullptr, "Unknown4"},
24 {4, nullptr, "Unknown5"},
25 {5, nullptr, "Unknown6"},
26 {6, nullptr, "Unknown7"},
27 };
28 // clang-format on
29
30 RegisterHandlers(functions);
31 }
32};
33
34class IPmModule final : public ServiceFramework<IPmModule> {
35public:
36 explicit IPmModule() : ServiceFramework{"IPmModule"} {
37 // clang-format off
38 static const FunctionInfo functions[] = {
39 {0, nullptr, "Initialize"},
40 {1, nullptr, "GetRequest"},
41 {2, nullptr, "Acknowledge"},
42 {3, nullptr, "Unknown1"},
43 };
44 // clang-format on
45
46 RegisterHandlers(functions);
47 }
48};
49
50class PSC_M final : public ServiceFramework<PSC_M> {
51public:
52 explicit PSC_M() : ServiceFramework{"psc:m"} {
53 // clang-format off
54 static const FunctionInfo functions[] = {
55 {0, &PSC_M::GetPmModule, "GetPmModule"},
56 };
57 // clang-format on
58
59 RegisterHandlers(functions);
60 }
61
62private:
63 void GetPmModule(Kernel::HLERequestContext& ctx) {
64 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
65 rb.Push(RESULT_SUCCESS);
66 rb.PushIpcInterface<IPmModule>();
67
68 LOG_DEBUG(Service_PSC, "called");
69 }
70};
71
72void InstallInterfaces(SM::ServiceManager& sm) {
73 std::make_shared<PSC_C>()->InstallAsService(sm);
74 std::make_shared<PSC_M>()->InstallAsService(sm);
75}
76
77} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h
new file mode 100644
index 000000000..5052eb02c
--- /dev/null
+++ b/src/core/hle/service/psc/psc.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::PSC {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::PSC
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 8b84fd349..889cdd41a 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -19,28 +19,42 @@
19#include "core/hle/service/am/am.h" 19#include "core/hle/service/am/am.h"
20#include "core/hle/service/aoc/aoc_u.h" 20#include "core/hle/service/aoc/aoc_u.h"
21#include "core/hle/service/apm/apm.h" 21#include "core/hle/service/apm/apm.h"
22#include "core/hle/service/arp/arp.h"
22#include "core/hle/service/audio/audio.h" 23#include "core/hle/service/audio/audio.h"
23#include "core/hle/service/bcat/bcat.h" 24#include "core/hle/service/bcat/bcat.h"
25#include "core/hle/service/bpc/bpc.h"
26#include "core/hle/service/btdrv/btdrv.h"
27#include "core/hle/service/btm/btm.h"
28#include "core/hle/service/caps/caps.h"
24#include "core/hle/service/erpt/erpt.h" 29#include "core/hle/service/erpt/erpt.h"
25#include "core/hle/service/es/es.h" 30#include "core/hle/service/es/es.h"
26#include "core/hle/service/eupld/eupld.h" 31#include "core/hle/service/eupld/eupld.h"
27#include "core/hle/service/fatal/fatal.h" 32#include "core/hle/service/fatal/fatal.h"
33#include "core/hle/service/fgm/fgm.h"
28#include "core/hle/service/filesystem/filesystem.h" 34#include "core/hle/service/filesystem/filesystem.h"
29#include "core/hle/service/friend/friend.h" 35#include "core/hle/service/friend/friend.h"
30#include "core/hle/service/grc/grc.h" 36#include "core/hle/service/grc/grc.h"
31#include "core/hle/service/hid/hid.h" 37#include "core/hle/service/hid/hid.h"
38#include "core/hle/service/lbl/lbl.h"
32#include "core/hle/service/ldn/ldn.h" 39#include "core/hle/service/ldn/ldn.h"
33#include "core/hle/service/ldr/ldr.h" 40#include "core/hle/service/ldr/ldr.h"
34#include "core/hle/service/lm/lm.h" 41#include "core/hle/service/lm/lm.h"
42#include "core/hle/service/mig/mig.h"
43#include "core/hle/service/mii/mii.h"
35#include "core/hle/service/mm/mm_u.h" 44#include "core/hle/service/mm/mm_u.h"
45#include "core/hle/service/ncm/ncm.h"
46#include "core/hle/service/nfc/nfc.h"
36#include "core/hle/service/nfp/nfp.h" 47#include "core/hle/service/nfp/nfp.h"
37#include "core/hle/service/nifm/nifm.h" 48#include "core/hle/service/nifm/nifm.h"
38#include "core/hle/service/nim/nim.h" 49#include "core/hle/service/nim/nim.h"
39#include "core/hle/service/ns/ns.h" 50#include "core/hle/service/ns/ns.h"
40#include "core/hle/service/nvdrv/nvdrv.h" 51#include "core/hle/service/nvdrv/nvdrv.h"
52#include "core/hle/service/pcie/pcie.h"
41#include "core/hle/service/pctl/pctl.h" 53#include "core/hle/service/pctl/pctl.h"
54#include "core/hle/service/pcv/pcv.h"
42#include "core/hle/service/pm/pm.h" 55#include "core/hle/service/pm/pm.h"
43#include "core/hle/service/prepo/prepo.h" 56#include "core/hle/service/prepo/prepo.h"
57#include "core/hle/service/psc/psc.h"
44#include "core/hle/service/service.h" 58#include "core/hle/service/service.h"
45#include "core/hle/service/set/settings.h" 59#include "core/hle/service/set/settings.h"
46#include "core/hle/service/sm/controller.h" 60#include "core/hle/service/sm/controller.h"
@@ -49,7 +63,9 @@
49#include "core/hle/service/spl/module.h" 63#include "core/hle/service/spl/module.h"
50#include "core/hle/service/ssl/ssl.h" 64#include "core/hle/service/ssl/ssl.h"
51#include "core/hle/service/time/time.h" 65#include "core/hle/service/time/time.h"
66#include "core/hle/service/usb/usb.h"
52#include "core/hle/service/vi/vi.h" 67#include "core/hle/service/vi/vi.h"
68#include "core/hle/service/wlan/wlan.h"
53 69
54using Kernel::ClientPort; 70using Kernel::ClientPort;
55using Kernel::ServerPort; 71using Kernel::ServerPort;
@@ -193,34 +209,50 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) {
193 AM::InstallInterfaces(*sm, nv_flinger); 209 AM::InstallInterfaces(*sm, nv_flinger);
194 AOC::InstallInterfaces(*sm); 210 AOC::InstallInterfaces(*sm);
195 APM::InstallInterfaces(*sm); 211 APM::InstallInterfaces(*sm);
196 BCAT::InstallInterfaces(*sm); 212 ARP::InstallInterfaces(*sm);
197 Audio::InstallInterfaces(*sm); 213 Audio::InstallInterfaces(*sm);
214 BCAT::InstallInterfaces(*sm);
215 BPC::InstallInterfaces(*sm);
216 BtDrv::InstallInterfaces(*sm);
217 BTM::InstallInterfaces(*sm);
218 Capture::InstallInterfaces(*sm);
198 ERPT::InstallInterfaces(*sm); 219 ERPT::InstallInterfaces(*sm);
199 ES::InstallInterfaces(*sm); 220 ES::InstallInterfaces(*sm);
200 EUPLD::InstallInterfaces(*sm); 221 EUPLD::InstallInterfaces(*sm);
201 Fatal::InstallInterfaces(*sm); 222 Fatal::InstallInterfaces(*sm);
223 FGM::InstallInterfaces(*sm);
202 FileSystem::InstallInterfaces(*sm); 224 FileSystem::InstallInterfaces(*sm);
203 Friend::InstallInterfaces(*sm); 225 Friend::InstallInterfaces(*sm);
204 GRC::InstallInterfaces(*sm); 226 GRC::InstallInterfaces(*sm);
205 HID::InstallInterfaces(*sm); 227 HID::InstallInterfaces(*sm);
228 LBL::InstallInterfaces(*sm);
206 LDN::InstallInterfaces(*sm); 229 LDN::InstallInterfaces(*sm);
207 LDR::InstallInterfaces(*sm); 230 LDR::InstallInterfaces(*sm);
208 LM::InstallInterfaces(*sm); 231 LM::InstallInterfaces(*sm);
232 Migration::InstallInterfaces(*sm);
233 Mii::InstallInterfaces(*sm);
209 MM::InstallInterfaces(*sm); 234 MM::InstallInterfaces(*sm);
235 NCM::InstallInterfaces(*sm);
236 NFC::InstallInterfaces(*sm);
210 NFP::InstallInterfaces(*sm); 237 NFP::InstallInterfaces(*sm);
211 NIFM::InstallInterfaces(*sm); 238 NIFM::InstallInterfaces(*sm);
212 NIM::InstallInterfaces(*sm); 239 NIM::InstallInterfaces(*sm);
213 NS::InstallInterfaces(*sm); 240 NS::InstallInterfaces(*sm);
214 Nvidia::InstallInterfaces(*sm); 241 Nvidia::InstallInterfaces(*sm);
242 PCIe::InstallInterfaces(*sm);
215 PCTL::InstallInterfaces(*sm); 243 PCTL::InstallInterfaces(*sm);
244 PCV::InstallInterfaces(*sm);
216 PlayReport::InstallInterfaces(*sm); 245 PlayReport::InstallInterfaces(*sm);
217 PM::InstallInterfaces(*sm); 246 PM::InstallInterfaces(*sm);
247 PSC::InstallInterfaces(*sm);
248 Set::InstallInterfaces(*sm);
218 Sockets::InstallInterfaces(*sm); 249 Sockets::InstallInterfaces(*sm);
219 SPL::InstallInterfaces(*sm); 250 SPL::InstallInterfaces(*sm);
220 SSL::InstallInterfaces(*sm); 251 SSL::InstallInterfaces(*sm);
221 Time::InstallInterfaces(*sm); 252 Time::InstallInterfaces(*sm);
253 USB::InstallInterfaces(*sm);
222 VI::InstallInterfaces(*sm, nv_flinger); 254 VI::InstallInterfaces(*sm, nv_flinger);
223 Set::InstallInterfaces(*sm); 255 WLAN::InstallInterfaces(*sm);
224 256
225 LOG_DEBUG(Service, "initialized OK"); 257 LOG_DEBUG(Service, "initialized OK");
226} 258}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 180f22703..046c5e18d 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -8,10 +8,9 @@
8#include <string> 8#include <string>
9#include <unordered_map> 9#include <unordered_map>
10#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
11#include "common/bit_field.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
14#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/object.h"
15 14
16//////////////////////////////////////////////////////////////////////////////////////////////////// 15////////////////////////////////////////////////////////////////////////////////////////////////////
17// Namespace Service 16// Namespace Service
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 1651f6122..a461e72ec 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -8,6 +8,7 @@
8#include "core/hle/kernel/client_port.h" 8#include "core/hle/kernel/client_port.h"
9#include "core/hle/kernel/client_session.h" 9#include "core/hle/kernel/client_session.h"
10#include "core/hle/service/set/set.h" 10#include "core/hle/service/set/set.h"
11#include "core/settings.h"
11 12
12namespace Service::Set { 13namespace Service::Set {
13 14
@@ -31,6 +32,10 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
31 LanguageCode::ZH_HANT, 32 LanguageCode::ZH_HANT,
32}}; 33}};
33 34
35LanguageCode GetLanguageCodeFromIndex(size_t index) {
36 return available_language_codes.at(index);
37}
38
34void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { 39void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
35 ctx.WriteBuffer(available_language_codes); 40 ctx.WriteBuffer(available_language_codes);
36 41
@@ -49,9 +54,17 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
49 LOG_DEBUG(Service_SET, "called"); 54 LOG_DEBUG(Service_SET, "called");
50} 55}
51 56
57void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
58 IPC::ResponseBuilder rb{ctx, 4};
59 rb.Push(RESULT_SUCCESS);
60 rb.Push(static_cast<u64>(available_language_codes[Settings::values.language_index]));
61
62 LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
63}
64
52SET::SET() : ServiceFramework("set") { 65SET::SET() : ServiceFramework("set") {
53 static const FunctionInfo functions[] = { 66 static const FunctionInfo functions[] = {
54 {0, nullptr, "GetLanguageCode"}, 67 {0, &SET::GetLanguageCode, "GetLanguageCode"},
55 {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, 68 {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
56 {2, nullptr, "MakeLanguageCode"}, 69 {2, nullptr, "MakeLanguageCode"},
57 {3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"}, 70 {3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index a2472ec4c..4232b6162 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -28,6 +28,7 @@ enum class LanguageCode : u64 {
28 ZH_HANS = 0x00736E61482D687A, 28 ZH_HANS = 0x00736E61482D687A,
29 ZH_HANT = 0x00746E61482D687A, 29 ZH_HANT = 0x00746E61482D687A,
30}; 30};
31LanguageCode GetLanguageCodeFromIndex(size_t idx);
31 32
32class SET final : public ServiceFramework<SET> { 33class SET final : public ServiceFramework<SET> {
33public: 34public:
@@ -35,6 +36,7 @@ public:
35 ~SET() = default; 36 ~SET() = default;
36 37
37private: 38private:
39 void GetLanguageCode(Kernel::HLERequestContext& ctx);
38 void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); 40 void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx);
39 void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx); 41 void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
40}; 42};
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index e2a00e4f6..e8ea62f08 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -4,9 +4,11 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <string> 8#include <string>
8#include <unordered_map> 9#include <unordered_map>
9#include "core/hle/kernel/kernel.h" 10
11#include "core/hle/kernel/object.h"
10#include "core/hle/result.h" 12#include "core/hle/result.h"
11#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
12 14
@@ -19,6 +21,8 @@ class SessionRequestHandler;
19 21
20namespace Service::SM { 22namespace Service::SM {
21 23
24class Controller;
25
22/// Interface to "sm:" service 26/// Interface to "sm:" service
23class SM final : public ServiceFramework<SM> { 27class SM final : public ServiceFramework<SM> {
24public: 28public:
@@ -32,8 +36,6 @@ private:
32 std::shared_ptr<ServiceManager> service_manager; 36 std::shared_ptr<ServiceManager> service_manager;
33}; 37};
34 38
35class Controller;
36
37constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1); 39constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1);
38constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1); 40constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
39constexpr ResultCode ERR_INVALID_NAME_SIZE(-1); 41constexpr ResultCode ERR_INVALID_NAME_SIZE(-1);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 37b58bb77..2172c681b 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -80,8 +80,8 @@ public:
80 {5, nullptr, "GetTimeZoneRuleVersion"}, 80 {5, nullptr, "GetTimeZoneRuleVersion"},
81 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, 81 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
82 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, 82 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
83 {200, nullptr, "ToPosixTime"}, 83 {201, nullptr, "ToPosixTime"},
84 {201, nullptr, "ToPosixTimeWithMyRule"}, 84 {202, nullptr, "ToPosixTimeWithMyRule"},
85 }; 85 };
86 RegisterHandlers(functions); 86 RegisterHandlers(functions);
87 } 87 }
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
new file mode 100644
index 000000000..e7fb5a419
--- /dev/null
+++ b/src/core/hle/service/usb/usb.cpp
@@ -0,0 +1,238 @@
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/kernel/hle_ipc.h"
10#include "core/hle/service/service.h"
11#include "core/hle/service/sm/sm.h"
12#include "core/hle/service/usb/usb.h"
13
14namespace Service::USB {
15
16class IDsInterface final : public ServiceFramework<IDsInterface> {
17public:
18 explicit IDsInterface() : ServiceFramework{"IDsInterface"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "GetDsEndpoint"},
22 {1, nullptr, "GetSetupEvent"},
23 {2, nullptr, "Unknown"},
24 {3, nullptr, "EnableInterface"},
25 {4, nullptr, "DisableInterface"},
26 {5, nullptr, "CtrlInPostBufferAsync"},
27 {6, nullptr, "CtrlOutPostBufferAsync"},
28 {7, nullptr, "GetCtrlInCompletionEvent"},
29 {8, nullptr, "GetCtrlInReportData"},
30 {9, nullptr, "GetCtrlOutCompletionEvent"},
31 {10, nullptr, "GetCtrlOutReportData"},
32 {11, nullptr, "StallCtrl"},
33 {12, nullptr, "AppendConfigurationData"},
34 };
35 // clang-format on
36
37 RegisterHandlers(functions);
38 }
39};
40
41class USB_DS final : public ServiceFramework<USB_DS> {
42public:
43 explicit USB_DS() : ServiceFramework{"usb:ds"} {
44 // clang-format off
45 static const FunctionInfo functions[] = {
46 {0, nullptr, "BindDevice"},
47 {1, nullptr, "BindClientProcess"},
48 {2, nullptr, "GetDsInterface"},
49 {3, nullptr, "GetStateChangeEvent"},
50 {4, nullptr, "GetState"},
51 {5, nullptr, "ClearDeviceData"},
52 {6, nullptr, "AddUsbStringDescriptor"},
53 {7, nullptr, "DeleteUsbStringDescriptor"},
54 {8, nullptr, "SetUsbDeviceDescriptor"},
55 {9, nullptr, "SetBinaryObjectStore"},
56 {10, nullptr, "Enable"},
57 {11, nullptr, "Disable"},
58 };
59 // clang-format on
60
61 RegisterHandlers(functions);
62 }
63};
64
65class IClientEpSession final : public ServiceFramework<IClientEpSession> {
66public:
67 explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} {
68 // clang-format off
69 static const FunctionInfo functions[] = {
70 {0, nullptr, "Unknown1"},
71 {1, nullptr, "Unknown2"},
72 {2, nullptr, "Unknown3"},
73 {3, nullptr, "Unknown4"},
74 {4, nullptr, "PostBufferAsync"},
75 {5, nullptr, "Unknown5"},
76 {6, nullptr, "Unknown6"},
77 {7, nullptr, "Unknown7"},
78 {8, nullptr, "Unknown8"},
79 };
80 // clang-format on
81
82 RegisterHandlers(functions);
83 }
84};
85
86class IClientIfSession final : public ServiceFramework<IClientIfSession> {
87public:
88 explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} {
89 // clang-format off
90 static const FunctionInfo functions[] = {
91 {0, nullptr, "Unknown1"},
92 {1, nullptr, "Unknown2"},
93 {2, nullptr, "Unknown3"},
94 {3, nullptr, "Unknown4"},
95 {4, nullptr, "Unknown5"},
96 {5, nullptr, "CtrlXferAsync"},
97 {6, nullptr, "Unknown6"},
98 {7, nullptr, "GetCtrlXferReport"},
99 {8, nullptr, "Unknown7"},
100 {9, nullptr, "GetClientEpSession"},
101 };
102 // clang-format on
103
104 RegisterHandlers(functions);
105 }
106};
107
108class USB_HS final : public ServiceFramework<USB_HS> {
109public:
110 explicit USB_HS() : ServiceFramework{"usb:hs"} {
111 // clang-format off
112 static const FunctionInfo functions[] = {
113 {0, nullptr, "BindClientProcess"},
114 {1, nullptr, "Unknown1"},
115 {2, nullptr, "Unknown2"},
116 {3, nullptr, "Unknown3"},
117 {4, nullptr, "Unknown4"},
118 {5, nullptr, "Unknown5"},
119 {6, nullptr, "GetInterfaceStateChangeEvent"},
120 {7, nullptr, "GetClientIfSession"},
121 };
122 // clang-format on
123
124 RegisterHandlers(functions);
125 }
126};
127
128class IPdSession final : public ServiceFramework<IPdSession> {
129public:
130 explicit IPdSession() : ServiceFramework{"IPdSession"} {
131 // clang-format off
132 static const FunctionInfo functions[] = {
133 {0, nullptr, "BindNoticeEvent"},
134 {1, nullptr, "Unknown1"},
135 {2, nullptr, "GetStatus"},
136 {3, nullptr, "GetNotice"},
137 {4, nullptr, "Unknown2"},
138 {5, nullptr, "Unknown3"},
139 {6, nullptr, "ReplyPowerRequest"},
140 };
141 // clang-format on
142
143 RegisterHandlers(functions);
144 }
145};
146
147class USB_PD final : public ServiceFramework<USB_PD> {
148public:
149 explicit USB_PD() : ServiceFramework{"usb:pd"} {
150 // clang-format off
151 static const FunctionInfo functions[] = {
152 {0, &USB_PD::GetPdSession, "GetPdSession"},
153 };
154 // clang-format on
155
156 RegisterHandlers(functions);
157 }
158
159private:
160 void GetPdSession(Kernel::HLERequestContext& ctx) {
161 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
162 rb.Push(RESULT_SUCCESS);
163 rb.PushIpcInterface<IPdSession>();
164
165 LOG_DEBUG(Service_USB, "called");
166 }
167};
168
169class IPdCradleSession final : public ServiceFramework<IPdCradleSession> {
170public:
171 explicit IPdCradleSession() : ServiceFramework{"IPdCradleSession"} {
172 // clang-format off
173 static const FunctionInfo functions[] = {
174 {0, nullptr, "VdmUserWrite"},
175 {1, nullptr, "VdmUserRead"},
176 {2, nullptr, "Vdm20Init"},
177 {3, nullptr, "GetFwType"},
178 {4, nullptr, "GetFwRevision"},
179 {5, nullptr, "GetManufacturerId"},
180 {6, nullptr, "GetDeviceId"},
181 {7, nullptr, "Unknown1"},
182 {8, nullptr, "Unknown2"},
183 };
184 // clang-format on
185
186 RegisterHandlers(functions);
187 }
188};
189
190class USB_PD_C final : public ServiceFramework<USB_PD_C> {
191public:
192 explicit USB_PD_C() : ServiceFramework{"usb:pd:c"} {
193 // clang-format off
194 static const FunctionInfo functions[] = {
195 {0, &USB_PD_C::GetPdCradleSession, "GetPdCradleSession"},
196 };
197 // clang-format on
198
199 RegisterHandlers(functions);
200 }
201
202private:
203 void GetPdCradleSession(Kernel::HLERequestContext& ctx) {
204 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
205 rb.Push(RESULT_SUCCESS);
206 rb.PushIpcInterface<IPdCradleSession>();
207
208 LOG_DEBUG(Service_USB, "called");
209 }
210};
211
212class USB_PM final : public ServiceFramework<USB_PM> {
213public:
214 explicit USB_PM() : ServiceFramework{"usb:pm"} {
215 // clang-format off
216 static const FunctionInfo functions[] = {
217 {0, nullptr, "Unknown1"},
218 {1, nullptr, "Unknown2"},
219 {2, nullptr, "Unknown3"},
220 {3, nullptr, "Unknown4"},
221 {4, nullptr, "Unknown5"},
222 {5, nullptr, "Unknown6"},
223 };
224 // clang-format on
225
226 RegisterHandlers(functions);
227 }
228};
229
230void InstallInterfaces(SM::ServiceManager& sm) {
231 std::make_shared<USB_DS>()->InstallAsService(sm);
232 std::make_shared<USB_HS>()->InstallAsService(sm);
233 std::make_shared<USB_PD>()->InstallAsService(sm);
234 std::make_shared<USB_PD_C>()->InstallAsService(sm);
235 std::make_shared<USB_PM>()->InstallAsService(sm);
236}
237
238} // namespace Service::USB
diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h
new file mode 100644
index 000000000..970a11fe8
--- /dev/null
+++ b/src/core/hle/service/usb/usb.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::USB {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::USB
diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp
new file mode 100644
index 000000000..2654594c1
--- /dev/null
+++ b/src/core/hle/service/wlan/wlan.cpp
@@ -0,0 +1,172 @@
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/service.h"
8#include "core/hle/service/sm/sm.h"
9#include "core/hle/service/wlan/wlan.h"
10
11namespace Service::WLAN {
12
13class WLANInfra final : public ServiceFramework<WLANInfra> {
14public:
15 explicit WLANInfra() : ServiceFramework{"wlan:inf"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "Unknown1"},
19 {1, nullptr, "Unknown2"},
20 {2, nullptr, "GetMacAddress"},
21 {3, nullptr, "StartScan"},
22 {4, nullptr, "StopScan"},
23 {5, nullptr, "Connect"},
24 {6, nullptr, "CancelConnect"},
25 {7, nullptr, "Disconnect"},
26 {8, nullptr, "Unknown3"},
27 {9, nullptr, "Unknown4"},
28 {10, nullptr, "GetState"},
29 {11, nullptr, "GetScanResult"},
30 {12, nullptr, "GetRssi"},
31 {13, nullptr, "ChangeRxAntenna"},
32 {14, nullptr, "Unknown5"},
33 {15, nullptr, "Unknown6"},
34 {16, nullptr, "RequestWakeUp"},
35 {17, nullptr, "RequestIfUpDown"},
36 {18, nullptr, "Unknown7"},
37 {19, nullptr, "Unknown8"},
38 {20, nullptr, "Unknown9"},
39 {21, nullptr, "Unknown10"},
40 {22, nullptr, "Unknown11"},
41 {23, nullptr, "Unknown12"},
42 {24, nullptr, "Unknown13"},
43 {25, nullptr, "Unknown14"},
44 {26, nullptr, "Unknown15"},
45 {27, nullptr, "Unknown16"},
46 };
47 // clang-format on
48
49 RegisterHandlers(functions);
50 }
51};
52
53class WLANLocal final : public ServiceFramework<WLANLocal> {
54public:
55 explicit WLANLocal() : ServiceFramework{"wlan:lcl"} {
56 // clang-format off
57 static const FunctionInfo functions[] = {
58 {0, nullptr, "Unknown1"},
59 {1, nullptr, "Unknown2"},
60 {2, nullptr, "Unknown3"},
61 {3, nullptr, "Unknown4"},
62 {4, nullptr, "Unknown5"},
63 {5, nullptr, "Unknown6"},
64 {6, nullptr, "GetMacAddress"},
65 {7, nullptr, "CreateBss"},
66 {8, nullptr, "DestroyBss"},
67 {9, nullptr, "StartScan"},
68 {10, nullptr, "StopScan"},
69 {11, nullptr, "Connect"},
70 {12, nullptr, "CancelConnect"},
71 {13, nullptr, "Join"},
72 {14, nullptr, "CancelJoin"},
73 {15, nullptr, "Disconnect"},
74 {16, nullptr, "SetBeaconLostCount"},
75 {17, nullptr, "Unknown7"},
76 {18, nullptr, "Unknown8"},
77 {19, nullptr, "Unknown9"},
78 {20, nullptr, "GetBssIndicationEvent"},
79 {21, nullptr, "GetBssIndicationInfo"},
80 {22, nullptr, "GetState"},
81 {23, nullptr, "GetAllowedChannels"},
82 {24, nullptr, "AddIe"},
83 {25, nullptr, "DeleteIe"},
84 {26, nullptr, "Unknown10"},
85 {27, nullptr, "Unknown11"},
86 {28, nullptr, "CreateRxEntry"},
87 {29, nullptr, "DeleteRxEntry"},
88 {30, nullptr, "Unknown12"},
89 {31, nullptr, "Unknown13"},
90 {32, nullptr, "AddMatchingDataToRxEntry"},
91 {33, nullptr, "RemoveMatchingDataFromRxEntry"},
92 {34, nullptr, "GetScanResult"},
93 {35, nullptr, "Unknown14"},
94 {36, nullptr, "SetActionFrameWithBeacon"},
95 {37, nullptr, "CancelActionFrameWithBeacon"},
96 {38, nullptr, "CreateRxEntryForActionFrame"},
97 {39, nullptr, "DeleteRxEntryForActionFrame"},
98 {40, nullptr, "Unknown15"},
99 {41, nullptr, "Unknown16"},
100 {42, nullptr, "CancelGetActionFrame"},
101 {43, nullptr, "GetRssi"},
102 {44, nullptr, "Unknown17"},
103 {45, nullptr, "Unknown18"},
104 {46, nullptr, "Unknown19"},
105 {47, nullptr, "Unknown20"},
106 {48, nullptr, "Unknown21"},
107 };
108 // clang-format on
109
110 RegisterHandlers(functions);
111 }
112};
113
114class WLANLocalGetFrame final : public ServiceFramework<WLANLocalGetFrame> {
115public:
116 explicit WLANLocalGetFrame() : ServiceFramework{"wlan:lg"} {
117 // clang-format off
118 static const FunctionInfo functions[] = {
119 {0, nullptr, "Unknown"},
120 };
121 // clang-format on
122
123 RegisterHandlers(functions);
124 }
125};
126
127class WLANSocketGetFrame final : public ServiceFramework<WLANSocketGetFrame> {
128public:
129 explicit WLANSocketGetFrame() : ServiceFramework{"wlan:sg"} {
130 // clang-format off
131 static const FunctionInfo functions[] = {
132 {0, nullptr, "Unknown"},
133 };
134 // clang-format on
135
136 RegisterHandlers(functions);
137 }
138};
139
140class WLANSocketManager final : public ServiceFramework<WLANSocketManager> {
141public:
142 explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} {
143 // clang-format off
144 static const FunctionInfo functions[] = {
145 {0, nullptr, "Unknown1"},
146 {1, nullptr, "Unknown2"},
147 {2, nullptr, "Unknown3"},
148 {3, nullptr, "Unknown4"},
149 {4, nullptr, "Unknown5"},
150 {5, nullptr, "Unknown6"},
151 {6, nullptr, "GetMacAddress"},
152 {7, nullptr, "SwitchTsfTimerFunction"},
153 {8, nullptr, "Unknown7"},
154 {9, nullptr, "Unknown8"},
155 {10, nullptr, "Unknown9"},
156 {11, nullptr, "Unknown10"},
157 };
158 // clang-format on
159
160 RegisterHandlers(functions);
161 }
162};
163
164void InstallInterfaces(SM::ServiceManager& sm) {
165 std::make_shared<WLANInfra>()->InstallAsService(sm);
166 std::make_shared<WLANLocal>()->InstallAsService(sm);
167 std::make_shared<WLANLocalGetFrame>()->InstallAsService(sm);
168 std::make_shared<WLANSocketGetFrame>()->InstallAsService(sm);
169 std::make_shared<WLANSocketManager>()->InstallAsService(sm);
170}
171
172} // namespace Service::WLAN
diff --git a/src/core/hle/service/wlan/wlan.h b/src/core/hle/service/wlan/wlan.h
new file mode 100644
index 000000000..054ea928a
--- /dev/null
+++ b/src/core/hle/service/wlan/wlan.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::WLAN {
12
13void InstallInterfaces(SM::ServiceManager& sm);
14
15} // namespace Service::WLAN
diff --git a/src/core/hw/aes/ccm.cpp b/src/core/hw/aes/ccm.cpp
deleted file mode 100644
index 1ee37aaa4..000000000
--- a/src/core/hw/aes/ccm.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "common/alignment.h"
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hw/aes/ccm.h"
10#include "core/hw/aes/key.h"
11
12namespace HW {
13namespace AES {
14
15std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& nonce,
16 size_t slot_id) {
17 UNIMPLEMENTED();
18 return {};
19}
20
21std::vector<u8> DecryptVerifyCCM(const std::vector<u8>& cipher, const CCMNonce& nonce,
22 size_t slot_id) {
23 UNIMPLEMENTED();
24 return {};
25}
26
27} // namespace AES
28} // namespace HW
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
deleted file mode 100644
index 2f48068c1..000000000
--- a/src/core/hw/hw.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
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/logging/log.h"
7#include "core/hw/hw.h"
8#include "core/hw/lcd.h"
9
10namespace HW {
11
12template <typename T>
13inline void Read(T& var, const u32 addr) {
14 switch (addr & 0xFFFFF000) {
15 case VADDR_GPU:
16 case VADDR_GPU + 0x1000:
17 case VADDR_GPU + 0x2000:
18 case VADDR_GPU + 0x3000:
19 case VADDR_GPU + 0x4000:
20 case VADDR_GPU + 0x5000:
21 case VADDR_GPU + 0x6000:
22 case VADDR_GPU + 0x7000:
23 case VADDR_GPU + 0x8000:
24 case VADDR_GPU + 0x9000:
25 case VADDR_GPU + 0xA000:
26 case VADDR_GPU + 0xB000:
27 case VADDR_GPU + 0xC000:
28 case VADDR_GPU + 0xD000:
29 case VADDR_GPU + 0xE000:
30 case VADDR_GPU + 0xF000:
31 break;
32 case VADDR_LCD:
33 LCD::Read(var, addr);
34 break;
35 default:
36 LOG_ERROR(HW_Memory, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr);
37 break;
38 }
39}
40
41template <typename T>
42inline void Write(u32 addr, const T data) {
43 switch (addr & 0xFFFFF000) {
44 case VADDR_GPU:
45 case VADDR_GPU + 0x1000:
46 case VADDR_GPU + 0x2000:
47 case VADDR_GPU + 0x3000:
48 case VADDR_GPU + 0x4000:
49 case VADDR_GPU + 0x5000:
50 case VADDR_GPU + 0x6000:
51 case VADDR_GPU + 0x7000:
52 case VADDR_GPU + 0x8000:
53 case VADDR_GPU + 0x9000:
54 case VADDR_GPU + 0xA000:
55 case VADDR_GPU + 0xB000:
56 case VADDR_GPU + 0xC000:
57 case VADDR_GPU + 0xD000:
58 case VADDR_GPU + 0xE000:
59 case VADDR_GPU + 0xF000:
60 break;
61 case VADDR_LCD:
62 LCD::Write(addr, data);
63 break;
64 default:
65 LOG_ERROR(HW_Memory, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr);
66 break;
67 }
68}
69
70// Explicitly instantiate template functions because we aren't defining this in the header:
71
72template void Read<u64>(u64& var, const u32 addr);
73template void Read<u32>(u32& var, const u32 addr);
74template void Read<u16>(u16& var, const u32 addr);
75template void Read<u8>(u8& var, const u32 addr);
76
77template void Write<u64>(u32 addr, const u64 data);
78template void Write<u32>(u32 addr, const u32 data);
79template void Write<u16>(u32 addr, const u16 data);
80template void Write<u8>(u32 addr, const u8 data);
81
82/// Update hardware
83void Update() {}
84
85/// Initialize hardware
86void Init() {
87 LCD::Init();
88 LOG_DEBUG(HW, "Initialized OK");
89}
90
91/// Shutdown hardware
92void Shutdown() {
93 LCD::Shutdown();
94 LOG_DEBUG(HW, "Shutdown OK");
95}
96} // namespace HW
diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h
deleted file mode 100644
index 5890d2b5c..000000000
--- a/src/core/hw/hw.h
+++ /dev/null
@@ -1,50 +0,0 @@
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 "common/common_types.h"
8
9namespace HW {
10
11/// Beginnings of IO register regions, in the user VA space.
12enum : u32 {
13 VADDR_HASH = 0x1EC01000,
14 VADDR_CSND = 0x1EC03000,
15 VADDR_DSP = 0x1EC40000,
16 VADDR_PDN = 0x1EC41000,
17 VADDR_CODEC = 0x1EC41000,
18 VADDR_SPI = 0x1EC42000,
19 VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM?
20 VADDR_I2C = 0x1EC44000,
21 VADDR_CODEC_2 = 0x1EC45000,
22 VADDR_HID = 0x1EC46000,
23 VADDR_GPIO = 0x1EC47000,
24 VADDR_I2C_2 = 0x1EC48000,
25 VADDR_SPI_3 = 0x1EC60000,
26 VADDR_I2C_3 = 0x1EC61000,
27 VADDR_MIC = 0x1EC62000,
28 VADDR_PXI = 0x1EC63000,
29 VADDR_LCD = 0x1ED02000,
30 VADDR_DSP_2 = 0x1ED03000,
31 VADDR_HASH_2 = 0x1EE01000,
32 VADDR_GPU = 0x1EF00000,
33};
34
35template <typename T>
36void Read(T& var, const u32 addr);
37
38template <typename T>
39void Write(u32 addr, const T data);
40
41/// Update hardware
42void Update();
43
44/// Initialize hardware
45void Init();
46
47/// Shutdown hardware
48void Shutdown();
49
50} // namespace HW
diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp
deleted file mode 100644
index 0b62174d5..000000000
--- a/src/core/hw/lcd.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
1// Copyright 2015 Citra Emulator Project
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 "common/logging/log.h"
8#include "core/hw/hw.h"
9#include "core/hw/lcd.h"
10#include "core/tracer/recorder.h"
11
12namespace LCD {
13
14Regs g_regs;
15
16template <typename T>
17inline void Read(T& var, const u32 raw_addr) {
18 u32 addr = raw_addr - HW::VADDR_LCD;
19 u32 index = addr / 4;
20
21 // Reads other than u32 are untested, so I'd rather have them abort than silently fail
22 if (index >= 0x400 || !std::is_same<T, u32>::value) {
23 LOG_ERROR(HW_LCD, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr);
24 return;
25 }
26
27 var = g_regs[index];
28}
29
30template <typename T>
31inline void Write(u32 addr, const T data) {
32 addr -= HW::VADDR_LCD;
33 u32 index = addr / 4;
34
35 // Writes other than u32 are untested, so I'd rather have them abort than silently fail
36 if (index >= 0x400 || !std::is_same<T, u32>::value) {
37 LOG_ERROR(HW_LCD, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr);
38 return;
39 }
40
41 g_regs[index] = static_cast<u32>(data);
42}
43
44// Explicitly instantiate template functions because we aren't defining this in the header:
45
46template void Read<u64>(u64& var, const u32 addr);
47template void Read<u32>(u32& var, const u32 addr);
48template void Read<u16>(u16& var, const u32 addr);
49template void Read<u8>(u8& var, const u32 addr);
50
51template void Write<u64>(u32 addr, const u64 data);
52template void Write<u32>(u32 addr, const u32 data);
53template void Write<u16>(u32 addr, const u16 data);
54template void Write<u8>(u32 addr, const u8 data);
55
56/// Initialize hardware
57void Init() {
58 memset(&g_regs, 0, sizeof(g_regs));
59 LOG_DEBUG(HW_LCD, "Initialized OK");
60}
61
62/// Shutdown hardware
63void Shutdown() {
64 LOG_DEBUG(HW_LCD, "Shutdown OK");
65}
66
67} // namespace LCD
diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h
deleted file mode 100644
index d2db9700f..000000000
--- a/src/core/hw/lcd.h
+++ /dev/null
@@ -1,86 +0,0 @@
1// Copyright 2015 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 <type_traits>
9#include "common/bit_field.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12
13#define LCD_REG_INDEX(field_name) (offsetof(LCD::Regs, field_name) / sizeof(u32))
14
15namespace LCD {
16
17struct Regs {
18
19 union ColorFill {
20 u32 raw;
21
22 BitField<0, 8, u32> color_r;
23 BitField<8, 8, u32> color_g;
24 BitField<16, 8, u32> color_b;
25 BitField<24, 1, u32> is_enabled;
26 };
27
28 INSERT_PADDING_WORDS(0x81);
29 ColorFill color_fill_top;
30 INSERT_PADDING_WORDS(0xE);
31 u32 backlight_top;
32
33 INSERT_PADDING_WORDS(0x1F0);
34
35 ColorFill color_fill_bottom;
36 INSERT_PADDING_WORDS(0xE);
37 u32 backlight_bottom;
38 INSERT_PADDING_WORDS(0x16F);
39
40 static constexpr size_t NumIds() {
41 return sizeof(Regs) / sizeof(u32);
42 }
43
44 const u32& operator[](int index) const {
45 const u32* content = reinterpret_cast<const u32*>(this);
46 return content[index];
47 }
48
49 u32& operator[](int index) {
50 u32* content = reinterpret_cast<u32*>(this);
51 return content[index];
52 }
53};
54static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout");
55
56// TODO: MSVC does not support using offsetof() on non-static data members even though this
57// is technically allowed since C++11. This macro should be enabled once MSVC adds
58// support for that.
59#ifndef _MSC_VER
60#define ASSERT_REG_POSITION(field_name, position) \
61 static_assert(offsetof(Regs, field_name) == position * 4, \
62 "Field " #field_name " has invalid position")
63
64ASSERT_REG_POSITION(color_fill_top, 0x81);
65ASSERT_REG_POSITION(backlight_top, 0x90);
66ASSERT_REG_POSITION(color_fill_bottom, 0x281);
67ASSERT_REG_POSITION(backlight_bottom, 0x290);
68
69#undef ASSERT_REG_POSITION
70#endif // !defined(_MSC_VER)
71
72extern Regs g_regs;
73
74template <typename T>
75void Read(T& var, const u32 addr);
76
77template <typename T>
78void Write(u32 addr, const T data);
79
80/// Initialize hardware
81void Init();
82
83/// Shutdown hardware
84void Shutdown();
85
86} // namespace LCD
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index b0277a875..9a8cdd0ff 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -20,6 +20,10 @@ namespace Loader {
20AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) 20AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
21 : AppLoader(std::move(file)) {} 21 : AppLoader(std::move(file)) {}
22 22
23AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
24 FileSys::VirtualDir directory)
25 : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {}
26
23FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { 27FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
24 if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { 28 if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
25 return FileType::DeconstructedRomDirectory; 29 return FileType::DeconstructedRomDirectory;
@@ -34,7 +38,12 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
34 return ResultStatus::ErrorAlreadyLoaded; 38 return ResultStatus::ErrorAlreadyLoaded;
35 } 39 }
36 40
37 const FileSys::VirtualDir dir = file->GetContainingDirectory(); 41 if (dir == nullptr) {
42 if (file == nullptr)
43 return ResultStatus::ErrorInvalidFormat;
44 dir = file->GetContainingDirectory();
45 }
46
38 const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); 47 const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
39 if (npdm == nullptr) 48 if (npdm == nullptr)
40 return ResultStatus::ErrorInvalidFormat; 49 return ResultStatus::ErrorInvalidFormat;
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 982a037f7..7d5433563 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -7,7 +7,7 @@
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/file_sys/program_metadata.h" 9#include "core/file_sys/program_metadata.h"
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/object.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13namespace Loader { 13namespace Loader {
@@ -22,6 +22,9 @@ class AppLoader_DeconstructedRomDirectory final : public AppLoader {
22public: 22public:
23 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); 23 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
24 24
25 // Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
26 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory);
27
25 /** 28 /**
26 * Returns the type of the file 29 * Returns the type of the file
27 * @param file std::shared_ptr<VfsFile> open file 30 * @param file std::shared_ptr<VfsFile> open file
@@ -40,6 +43,7 @@ public:
40private: 43private:
41 FileSys::ProgramMetadata metadata; 44 FileSys::ProgramMetadata metadata;
42 FileSys::VirtualFile romfs; 45 FileSys::VirtualFile romfs;
46 FileSys::VirtualDir dir;
43}; 47};
44 48
45} // namespace Loader 49} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 352938dcb..a7133f5a6 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -311,11 +311,11 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
311 CodeSet::Segment* codeset_segment; 311 CodeSet::Segment* codeset_segment;
312 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 312 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
313 if (permission_flags == (PF_R | PF_X)) { 313 if (permission_flags == (PF_R | PF_X)) {
314 codeset_segment = &codeset->code; 314 codeset_segment = &codeset->CodeSegment();
315 } else if (permission_flags == (PF_R)) { 315 } else if (permission_flags == (PF_R)) {
316 codeset_segment = &codeset->rodata; 316 codeset_segment = &codeset->RODataSegment();
317 } else if (permission_flags == (PF_R | PF_W)) { 317 } else if (permission_flags == (PF_R | PF_W)) {
318 codeset_segment = &codeset->data; 318 codeset_segment = &codeset->DataSegment();
319 } else { 319 } else {
320 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 320 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
321 p->p_flags); 321 p->p_flags);
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index cbc4177c6..57e6c0365 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -13,6 +13,7 @@
13#include "core/loader/nca.h" 13#include "core/loader/nca.h"
14#include "core/loader/nro.h" 14#include "core/loader/nro.h"
15#include "core/loader/nso.h" 15#include "core/loader/nso.h"
16#include "core/loader/xci.h"
16 17
17namespace Loader { 18namespace Loader {
18 19
@@ -35,6 +36,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
35 CHECK_TYPE(NSO) 36 CHECK_TYPE(NSO)
36 CHECK_TYPE(NRO) 37 CHECK_TYPE(NRO)
37 CHECK_TYPE(NCA) 38 CHECK_TYPE(NCA)
39 CHECK_TYPE(XCI)
38 40
39#undef CHECK_TYPE 41#undef CHECK_TYPE
40 42
@@ -60,6 +62,8 @@ FileType GuessFromFilename(const std::string& name) {
60 return FileType::NSO; 62 return FileType::NSO;
61 if (extension == "nca") 63 if (extension == "nca")
62 return FileType::NCA; 64 return FileType::NCA;
65 if (extension == "xci")
66 return FileType::XCI;
63 67
64 return FileType::Unknown; 68 return FileType::Unknown;
65} 69}
@@ -74,6 +78,8 @@ const char* GetFileTypeString(FileType type) {
74 return "NSO"; 78 return "NSO";
75 case FileType::NCA: 79 case FileType::NCA:
76 return "NCA"; 80 return "NCA";
81 case FileType::XCI:
82 return "XCI";
77 case FileType::DeconstructedRomDirectory: 83 case FileType::DeconstructedRomDirectory:
78 return "Directory"; 84 return "Directory";
79 case FileType::Error: 85 case FileType::Error:
@@ -111,6 +117,9 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
111 case FileType::NCA: 117 case FileType::NCA:
112 return std::make_unique<AppLoader_NCA>(std::move(file)); 118 return std::make_unique<AppLoader_NCA>(std::move(file));
113 119
120 case FileType::XCI:
121 return std::make_unique<AppLoader_XCI>(std::move(file));
122
114 // NX deconstructed ROM directory. 123 // NX deconstructed ROM directory.
115 case FileType::DeconstructedRomDirectory: 124 case FileType::DeconstructedRomDirectory:
116 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); 125 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index fbf11e5d0..e69ab85ef 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -14,7 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/file_util.h" 15#include "common/file_util.h"
16#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
17#include "core/hle/kernel/kernel.h" 17#include "core/hle/kernel/object.h"
18 18
19namespace Kernel { 19namespace Kernel {
20struct AddressMapping; 20struct AddressMapping;
@@ -31,6 +31,7 @@ enum class FileType {
31 NSO, 31 NSO,
32 NRO, 32 NRO,
33 NCA, 33 NCA,
34 XCI,
34 DeconstructedRomDirectory, 35 DeconstructedRomDirectory,
35}; 36};
36 37
@@ -72,7 +73,8 @@ enum class ResultStatus {
72 ErrorNotUsed, 73 ErrorNotUsed,
73 ErrorAlreadyLoaded, 74 ErrorAlreadyLoaded,
74 ErrorMemoryAllocationFailed, 75 ErrorMemoryAllocationFailed,
75 ErrorEncrypted, 76 ErrorMissingKeys,
77 ErrorDecrypting,
76 ErrorUnsupportedArch, 78 ErrorUnsupportedArch,
77}; 79};
78 80
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index c80df23be..dbc67c0b5 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -22,15 +22,14 @@
22 22
23namespace Loader { 23namespace Loader {
24 24
25AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} 25AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_)
26 : AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {}
26 27
27FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { 28FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
28 // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. 29 FileSys::NCA nca(file);
29 FileSys::NCAHeader header{};
30 if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header))
31 return FileType::Error;
32 30
33 if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program) 31 if (nca.GetStatus() == ResultStatus::Success &&
32 nca.GetType() == FileSys::NCAContentType::Program)
34 return FileType::NCA; 33 return FileType::NCA;
35 34
36 return FileType::Error; 35 return FileType::Error;
@@ -41,8 +40,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
41 return ResultStatus::ErrorAlreadyLoaded; 40 return ResultStatus::ErrorAlreadyLoaded;
42 } 41 }
43 42
44 nca = std::make_unique<FileSys::NCA>(file); 43 const auto result = nca->GetStatus();
45 ResultStatus result = nca->GetStatus();
46 if (result != ResultStatus::Success) { 44 if (result != ResultStatus::Success) {
47 return result; 45 return result;
48 } 46 }
@@ -50,44 +48,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
50 if (nca->GetType() != FileSys::NCAContentType::Program) 48 if (nca->GetType() != FileSys::NCAContentType::Program)
51 return ResultStatus::ErrorInvalidFormat; 49 return ResultStatus::ErrorInvalidFormat;
52 50
53 auto exefs = nca->GetExeFS(); 51 const auto exefs = nca->GetExeFS();
54 52
55 if (exefs == nullptr) 53 if (exefs == nullptr)
56 return ResultStatus::ErrorInvalidFormat; 54 return ResultStatus::ErrorInvalidFormat;
57 55
58 result = metadata.Load(exefs->GetFile("main.npdm")); 56 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
59 if (result != ResultStatus::Success) {
60 return result;
61 }
62 metadata.Print();
63
64 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
65 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
66 return ResultStatus::ErrorUnsupportedArch;
67 }
68 57
69 VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; 58 const auto load_result = directory_loader->Load(process);
70 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 59 if (load_result != ResultStatus::Success)
71 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 60 return load_result;
72 const VAddr load_addr = next_load_addr;
73
74 next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
75 if (next_load_addr) {
76 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
77 // Register module with GDBStub
78 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
79 } else {
80 next_load_addr = load_addr;
81 }
82 }
83
84 process->program_id = metadata.GetTitleID();
85 process->svc_access_mask.set();
86 process->address_mappings = default_address_mappings;
87 process->resource_limit =
88 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
89 process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
90 metadata.GetMainThreadStackSize());
91 61
92 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) 62 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
93 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 63 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
@@ -98,12 +68,21 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
98} 68}
99 69
100ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { 70ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
101 if (nca == nullptr || nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) 71 if (nca == nullptr)
72 return ResultStatus::ErrorNotLoaded;
73 if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0)
102 return ResultStatus::ErrorNotUsed; 74 return ResultStatus::ErrorNotUsed;
103 dir = nca->GetRomFS(); 75 dir = nca->GetRomFS();
104 return ResultStatus::Success; 76 return ResultStatus::Success;
105} 77}
106 78
79ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
80 if (nca == nullptr)
81 return ResultStatus::ErrorNotLoaded;
82 out_program_id = nca->GetTitleId();
83 return ResultStatus::Success;
84}
85
107AppLoader_NCA::~AppLoader_NCA() = default; 86AppLoader_NCA::~AppLoader_NCA() = default;
108 87
109} // namespace Loader 88} // namespace Loader
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 52c95953a..0fd2d0417 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -8,8 +8,9 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/program_metadata.h" 10#include "core/file_sys/program_metadata.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/object.h"
12#include "core/loader/loader.h" 12#include "core/loader/loader.h"
13#include "deconstructed_rom_directory.h"
13 14
14namespace Loader { 15namespace Loader {
15 16
@@ -33,12 +34,15 @@ public:
33 34
34 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 35 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
35 36
37 ResultStatus ReadProgramId(u64& out_program_id) override;
38
36 ~AppLoader_NCA(); 39 ~AppLoader_NCA();
37 40
38private: 41private:
39 FileSys::ProgramMetadata metadata; 42 FileSys::ProgramMetadata metadata;
40 43
41 std::unique_ptr<FileSys::NCA> nca; 44 std::unique_ptr<FileSys::NCA> nca;
45 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
42}; 46};
43 47
44} // namespace Loader 48} // namespace Loader
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 7d3ec2a76..dc053cdad 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -159,7 +159,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
159 // Resize program image to include .bss section and page align each section 159 // Resize program image to include .bss section and page align each section
160 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 160 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
161 } 161 }
162 codeset->data.size += bss_size; 162 codeset->DataSegment().size += bss_size;
163 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 163 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
164 164
165 // Load codeset for current process 165 // Load codeset for current process
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04a0f497e..bb01c9e25 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -6,7 +6,7 @@
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/object.h"
10#include "core/loader/linker.h" 10#include "core/loader/linker.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 06b1b33f4..fee7d58c6 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -127,7 +127,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
127 // Resize program image to include .bss section and page align each section 127 // Resize program image to include .bss section and page align each section
128 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); 128 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
129 } 129 }
130 codeset->data.size += bss_size; 130 codeset->DataSegment().size += bss_size;
131 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; 131 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
132 program_image.resize(image_size); 132 program_image.resize(image_size);
133 133
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 3f7567500..aaeb1f2a9 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -6,7 +6,7 @@
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/object.h"
10#include "core/loader/linker.h" 10#include "core/loader/linker.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
new file mode 100644
index 000000000..eb4dee2c2
--- /dev/null
+++ b/src/core/loader/xci.cpp
@@ -0,0 +1,74 @@
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 <vector>
6
7#include "common/file_util.h"
8#include "common/logging/log.h"
9#include "common/string_util.h"
10#include "common/swap.h"
11#include "core/core.h"
12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/control_metadata.h"
14#include "core/file_sys/program_metadata.h"
15#include "core/file_sys/romfs.h"
16#include "core/gdbstub/gdbstub.h"
17#include "core/hle/kernel/process.h"
18#include "core/hle/kernel/resource_limit.h"
19#include "core/hle/service/filesystem/filesystem.h"
20#include "core/loader/nso.h"
21#include "core/loader/xci.h"
22#include "core/memory.h"
23
24namespace Loader {
25
26AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
28 nca_loader(std::make_unique<AppLoader_NCA>(
29 xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {}
30
31AppLoader_XCI::~AppLoader_XCI() = default;
32
33FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
34 FileSys::XCI xci(file);
35
36 if (xci.GetStatus() == ResultStatus::Success &&
37 xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr &&
38 AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) ==
39 FileType::NCA) {
40 return FileType::XCI;
41 }
42
43 return FileType::Error;
44}
45
46ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
47 if (is_loaded) {
48 return ResultStatus::ErrorAlreadyLoaded;
49 }
50
51 if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) {
52 if (!Core::Crypto::KeyManager::KeyFileExists(false))
53 return ResultStatus::ErrorMissingKeys;
54 return ResultStatus::ErrorDecrypting;
55 }
56
57 auto result = nca_loader->Load(process);
58 if (result != ResultStatus::Success)
59 return result;
60
61 is_loaded = true;
62
63 return ResultStatus::Success;
64}
65
66ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& dir) {
67 return nca_loader->ReadRomFS(dir);
68}
69
70ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
71 return nca_loader->ReadProgramId(out_program_id);
72}
73
74} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
new file mode 100644
index 000000000..0dbcfbdf8
--- /dev/null
+++ b/src/core/loader/xci.h
@@ -0,0 +1,44 @@
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#include "common/common_types.h"
9#include "core/file_sys/card_image.h"
10#include "core/loader/loader.h"
11#include "core/loader/nca.h"
12
13namespace Loader {
14
15/// Loads an XCI file
16class AppLoader_XCI final : public AppLoader {
17public:
18 explicit AppLoader_XCI(FileSys::VirtualFile file);
19 ~AppLoader_XCI();
20
21 /**
22 * Returns the type of the file
23 * @param file std::shared_ptr<VfsFile> open file
24 * @return FileType found, or FileType::Error if this loader doesn't know it
25 */
26 static FileType IdentifyType(const FileSys::VirtualFile& file);
27
28 FileType GetFileType() override {
29 return IdentifyType(file);
30 }
31
32 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
33
34 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
35 ResultStatus ReadProgramId(u64& out_program_id) override;
36
37private:
38 FileSys::ProgramMetadata metadata;
39
40 std::unique_ptr<FileSys::XCI> xci;
41 std::unique_ptr<AppLoader_NCA> nca_loader;
42};
43
44} // namespace Loader
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index e753e3436..1133bcbaf 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -14,7 +14,6 @@
14#include "common/swap.h" 14#include "common/swap.h"
15#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
16#include "core/core.h" 16#include "core/core.h"
17#include "core/hle/kernel/memory.h"
18#include "core/hle/kernel/process.h" 17#include "core/hle/kernel/process.h"
19#include "core/hle/lock.h" 18#include "core/hle/lock.h"
20#include "core/memory.h" 19#include "core/memory.h"
@@ -24,8 +23,6 @@
24 23
25namespace Memory { 24namespace Memory {
26 25
27static std::array<u8, Memory::VRAM_SIZE> vram;
28
29static PageTable* current_page_table = nullptr; 26static PageTable* current_page_table = nullptr;
30 27
31void SetCurrentPageTable(PageTable* page_table) { 28void SetCurrentPageTable(PageTable* page_table) {
@@ -102,22 +99,6 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin
102} 99}
103 100
104/** 101/**
105 * This function should only be called for virtual addreses with attribute `PageType::Special`.
106 */
107static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_table, VAddr vaddr,
108 u64 size) {
109 std::set<MemoryHookPointer> result;
110 auto interval = boost::icl::discrete_interval<VAddr>::closed(vaddr, vaddr + size - 1);
111 auto interval_list = page_table.special_regions.equal_range(interval);
112 for (auto it = interval_list.first; it != interval_list.second; ++it) {
113 for (const auto& region : it->second) {
114 result.insert(region.handler);
115 }
116 }
117 return result;
118}
119
120/**
121 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) 102 * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned)
122 * using a VMA from the current process 103 * using a VMA from the current process
123 */ 104 */
@@ -242,10 +223,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) {
242 return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; 223 return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;
243} 224}
244 225
245bool IsValidPhysicalAddress(const PAddr paddr) {
246 return GetPhysicalPointer(paddr) != nullptr;
247}
248
249u8* GetPointer(const VAddr vaddr) { 226u8* GetPointer(const VAddr vaddr) {
250 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; 227 u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
251 if (page_pointer) { 228 if (page_pointer) {
@@ -274,61 +251,6 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) {
274 return string; 251 return string;
275} 252}
276 253
277u8* GetPhysicalPointer(PAddr address) {
278 struct MemoryArea {
279 PAddr paddr_base;
280 u32 size;
281 };
282
283 static constexpr MemoryArea memory_areas[] = {
284 {VRAM_PADDR, VRAM_SIZE},
285 {IO_AREA_PADDR, IO_AREA_SIZE},
286 {DSP_RAM_PADDR, DSP_RAM_SIZE},
287 {FCRAM_PADDR, FCRAM_N3DS_SIZE},
288 };
289
290 const auto area =
291 std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) {
292 return address >= area.paddr_base && address < area.paddr_base + area.size;
293 });
294
295 if (area == std::end(memory_areas)) {
296 LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ 0x{:016X}", address);
297 return nullptr;
298 }
299
300 if (area->paddr_base == IO_AREA_PADDR) {
301 LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr={:016X}", address);
302 return nullptr;
303 }
304
305 u64 offset_into_region = address - area->paddr_base;
306
307 u8* target_pointer = nullptr;
308 switch (area->paddr_base) {
309 case VRAM_PADDR:
310 target_pointer = vram.data() + offset_into_region;
311 break;
312 case DSP_RAM_PADDR:
313 break;
314 case FCRAM_PADDR:
315 for (const auto& region : Kernel::memory_regions) {
316 if (offset_into_region >= region.base &&
317 offset_into_region < region.base + region.size) {
318 target_pointer =
319 region.linear_heap_memory->data() + offset_into_region - region.base;
320 break;
321 }
322 }
323 ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address");
324 break;
325 default:
326 UNREACHABLE();
327 }
328
329 return target_pointer;
330}
331
332void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) { 254void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) {
333 if (gpu_addr == 0) { 255 if (gpu_addr == 0) {
334 return; 256 return;
@@ -404,43 +326,45 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached)
404} 326}
405 327
406void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { 328void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
329 auto& system_instance = Core::System::GetInstance();
330
407 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be 331 // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be
408 // null here 332 // null here
409 if (VideoCore::g_renderer == nullptr) { 333 if (!system_instance.IsPoweredOn()) {
410 return; 334 return;
411 } 335 }
412 336
413 VAddr end = start + size; 337 VAddr end = start + size;
414 338
415 auto CheckRegion = [&](VAddr region_start, VAddr region_end) { 339 const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
416 if (start >= region_end || end <= region_start) { 340 if (start >= region_end || end <= region_start) {
417 // No overlap with region 341 // No overlap with region
418 return; 342 return;
419 } 343 }
420 344
421 VAddr overlap_start = std::max(start, region_start); 345 const VAddr overlap_start = std::max(start, region_start);
422 VAddr overlap_end = std::min(end, region_end); 346 const VAddr overlap_end = std::min(end, region_end);
423 347
424 std::vector<Tegra::GPUVAddr> gpu_addresses = 348 const std::vector<Tegra::GPUVAddr> gpu_addresses =
425 Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start); 349 system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start);
426 350
427 if (gpu_addresses.empty()) { 351 if (gpu_addresses.empty()) {
428 return; 352 return;
429 } 353 }
430 354
431 u64 overlap_size = overlap_end - overlap_start; 355 const u64 overlap_size = overlap_end - overlap_start;
432 356
433 for (const auto& gpu_address : gpu_addresses) { 357 for (const auto& gpu_address : gpu_addresses) {
434 auto* rasterizer = VideoCore::g_renderer->Rasterizer(); 358 auto& rasterizer = system_instance.Renderer().Rasterizer();
435 switch (mode) { 359 switch (mode) {
436 case FlushMode::Flush: 360 case FlushMode::Flush:
437 rasterizer->FlushRegion(gpu_address, overlap_size); 361 rasterizer.FlushRegion(gpu_address, overlap_size);
438 break; 362 break;
439 case FlushMode::Invalidate: 363 case FlushMode::Invalidate:
440 rasterizer->InvalidateRegion(gpu_address, overlap_size); 364 rasterizer.InvalidateRegion(gpu_address, overlap_size);
441 break; 365 break;
442 case FlushMode::FlushAndInvalidate: 366 case FlushMode::FlushAndInvalidate:
443 rasterizer->FlushAndInvalidateRegion(gpu_address, overlap_size); 367 rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size);
444 break; 368 break;
445 } 369 }
446 } 370 }
@@ -666,48 +590,4 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size) {
666 CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); 590 CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size);
667} 591}
668 592
669boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) {
670 if (addr == 0) {
671 return 0;
672 } else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) {
673 return addr - VRAM_VADDR + VRAM_PADDR;
674 } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) {
675 return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR;
676 } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) {
677 return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR;
678 } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) {
679 return addr - DSP_RAM_VADDR + DSP_RAM_PADDR;
680 } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) {
681 return addr - IO_AREA_VADDR + IO_AREA_PADDR;
682 }
683
684 return boost::none;
685}
686
687PAddr VirtualToPhysicalAddress(const VAddr addr) {
688 auto paddr = TryVirtualToPhysicalAddress(addr);
689 if (!paddr) {
690 LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:016X}", addr);
691 // To help with debugging, set bit on address so that it's obviously invalid.
692 return addr | 0x80000000;
693 }
694 return *paddr;
695}
696
697boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) {
698 if (addr == 0) {
699 return 0;
700 } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) {
701 return addr - VRAM_PADDR + VRAM_VADDR;
702 } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) {
703 return addr - FCRAM_PADDR + Core::CurrentProcess()->GetLinearHeapAreaAddress();
704 } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) {
705 return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
706 } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
707 return addr - IO_AREA_PADDR + IO_AREA_VADDR;
708 }
709
710 return boost::none;
711}
712
713} // namespace Memory 593} // namespace Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index 8d5d017a4..b7fb3b9ed 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -6,12 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <map>
10#include <string> 9#include <string>
11#include <tuple> 10#include <tuple>
12#include <vector>
13#include <boost/icl/interval_map.hpp> 11#include <boost/icl/interval_map.hpp>
14#include <boost/optional.hpp>
15#include "common/common_types.h" 12#include "common/common_types.h"
16#include "core/memory_hook.h" 13#include "core/memory_hook.h"
17#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
@@ -85,40 +82,6 @@ struct PageTable {
85 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; 82 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
86}; 83};
87 84
88/// Physical memory regions as seen from the ARM11
89enum : PAddr {
90 /// IO register area
91 IO_AREA_PADDR = 0x10100000,
92 IO_AREA_SIZE = 0x01000000, ///< IO area size (16MB)
93 IO_AREA_PADDR_END = IO_AREA_PADDR + IO_AREA_SIZE,
94
95 /// MPCore internal memory region
96 MPCORE_RAM_PADDR = 0x17E00000,
97 MPCORE_RAM_SIZE = 0x00002000, ///< MPCore internal memory size (8KB)
98 MPCORE_RAM_PADDR_END = MPCORE_RAM_PADDR + MPCORE_RAM_SIZE,
99
100 /// Video memory
101 VRAM_PADDR = 0x18000000,
102 VRAM_SIZE = 0x00600000, ///< VRAM size (6MB)
103 VRAM_PADDR_END = VRAM_PADDR + VRAM_SIZE,
104
105 /// DSP memory
106 DSP_RAM_PADDR = 0x1FF00000,
107 DSP_RAM_SIZE = 0x00080000, ///< DSP memory size (512KB)
108 DSP_RAM_PADDR_END = DSP_RAM_PADDR + DSP_RAM_SIZE,
109
110 /// AXI WRAM
111 AXI_WRAM_PADDR = 0x1FF80000,
112 AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size (512KB)
113 AXI_WRAM_PADDR_END = AXI_WRAM_PADDR + AXI_WRAM_SIZE,
114
115 /// Main FCRAM
116 FCRAM_PADDR = 0x20000000,
117 FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB)
118 FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB)
119 FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE,
120};
121
122/// Virtual user-space memory regions 85/// Virtual user-space memory regions
123enum : VAddr { 86enum : VAddr {
124 /// Where the application text, data and bss reside. 87 /// Where the application text, data and bss reside.
@@ -126,24 +89,6 @@ enum : VAddr {
126 PROCESS_IMAGE_MAX_SIZE = 0x08000000, 89 PROCESS_IMAGE_MAX_SIZE = 0x08000000,
127 PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, 90 PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE,
128 91
129 /// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical
130 /// memory.
131 LINEAR_HEAP_VADDR = 0x14000000,
132 LINEAR_HEAP_SIZE = 0x08000000,
133 LINEAR_HEAP_VADDR_END = LINEAR_HEAP_VADDR + LINEAR_HEAP_SIZE,
134
135 /// Maps 1:1 to the IO register area.
136 IO_AREA_VADDR = 0x1EC00000,
137 IO_AREA_VADDR_END = IO_AREA_VADDR + IO_AREA_SIZE,
138
139 /// Maps 1:1 to VRAM.
140 VRAM_VADDR = 0x1F000000,
141 VRAM_VADDR_END = VRAM_VADDR + VRAM_SIZE,
142
143 /// Maps 1:1 to DSP memory.
144 DSP_RAM_VADDR = 0x1FF00000,
145 DSP_RAM_VADDR_END = DSP_RAM_VADDR + DSP_RAM_SIZE,
146
147 /// Read-only page containing kernel and system configuration values. 92 /// Read-only page containing kernel and system configuration values.
148 CONFIG_MEMORY_VADDR = 0x1FF80000, 93 CONFIG_MEMORY_VADDR = 0x1FF80000,
149 CONFIG_MEMORY_SIZE = 0x00001000, 94 CONFIG_MEMORY_SIZE = 0x00001000,
@@ -154,13 +99,8 @@ enum : VAddr {
154 SHARED_PAGE_SIZE = 0x00001000, 99 SHARED_PAGE_SIZE = 0x00001000,
155 SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, 100 SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
156 101
157 /// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS.
158 NEW_LINEAR_HEAP_VADDR = 0x30000000,
159 NEW_LINEAR_HEAP_SIZE = 0x10000000,
160 NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
161
162 /// Area where TLS (Thread-Local Storage) buffers are allocated. 102 /// Area where TLS (Thread-Local Storage) buffers are allocated.
163 TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END, 103 TLS_AREA_VADDR = 0x40000000,
164 TLS_ENTRY_SIZE = 0x200, 104 TLS_ENTRY_SIZE = 0x200,
165 TLS_AREA_SIZE = 0x10000000, 105 TLS_AREA_SIZE = 0x10000000,
166 TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, 106 TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
@@ -200,12 +140,10 @@ void SetCurrentPageTable(PageTable* page_table);
200PageTable* GetCurrentPageTable(); 140PageTable* GetCurrentPageTable();
201 141
202/// Determines if the given VAddr is valid for the specified process. 142/// Determines if the given VAddr is valid for the specified process.
203bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); 143bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr);
204bool IsValidVirtualAddress(const VAddr addr); 144bool IsValidVirtualAddress(VAddr vaddr);
205/// Determines if the given VAddr is a kernel address 145/// Determines if the given VAddr is a kernel address
206bool IsKernelVirtualAddress(const VAddr addr); 146bool IsKernelVirtualAddress(VAddr vaddr);
207
208bool IsValidPhysicalAddress(const PAddr addr);
209 147
210u8 Read8(VAddr addr); 148u8 Read8(VAddr addr);
211u16 Read16(VAddr addr); 149u16 Read16(VAddr addr);
@@ -217,42 +155,17 @@ void Write16(VAddr addr, u16 data);
217void Write32(VAddr addr, u32 data); 155void Write32(VAddr addr, u32 data);
218void Write64(VAddr addr, u64 data); 156void Write64(VAddr addr, u64 data);
219 157
220void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, 158void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size);
221 size_t size); 159void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size);
222void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); 160void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
223void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,
224 size_t size); 161 size_t size);
225void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); 162void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size);
226void ZeroBlock(const VAddr dest_addr, const size_t size); 163void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size);
227void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); 164void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size);
228 165
229u8* GetPointer(VAddr virtual_address); 166u8* GetPointer(VAddr vaddr);
230
231std::string ReadCString(VAddr virtual_address, std::size_t max_length);
232
233/**
234 * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical
235 * address. This should be used by services to translate addresses for use by the hardware.
236 */
237boost::optional<PAddr> TryVirtualToPhysicalAddress(VAddr addr);
238
239/**
240 * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical
241 * address. This should be used by services to translate addresses for use by the hardware.
242 *
243 * @deprecated Use TryVirtualToPhysicalAddress(), which reports failure.
244 */
245PAddr VirtualToPhysicalAddress(VAddr addr);
246 167
247/** 168std::string ReadCString(VAddr vaddr, std::size_t max_length);
248 * Undoes a mapping performed by VirtualToPhysicalAddress().
249 */
250boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr);
251
252/**
253 * Gets a pointer to the memory region beginning at the specified physical address.
254 */
255u8* GetPhysicalPointer(PAddr address);
256 169
257enum class FlushMode { 170enum class FlushMode {
258 /// Write back modified surfaces to RAM 171 /// Write back modified surfaces to RAM
@@ -266,7 +179,7 @@ enum class FlushMode {
266/** 179/**
267 * Mark each page touching the region as cached. 180 * Mark each page touching the region as cached.
268 */ 181 */
269void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached); 182void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached);
270 183
271/** 184/**
272 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual 185 * Flushes and invalidates any externally cached rasterizer resources touching the given virtual
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 5f53b16d3..8e09b9b63 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -40,22 +40,21 @@ void PerfStats::EndGameFrame() {
40 game_frames += 1; 40 game_frames += 1;
41} 41}
42 42
43PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { 43PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) {
44 std::lock_guard<std::mutex> lock(object_mutex); 44 std::lock_guard<std::mutex> lock(object_mutex);
45 45
46 auto now = Clock::now(); 46 const auto now = Clock::now();
47 // Walltime elapsed since stats were reset 47 // Walltime elapsed since stats were reset
48 auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); 48 const auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
49 49
50 auto system_us_per_second = 50 const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
51 static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
52 51
53 Results results{}; 52 Results results{};
54 results.system_fps = static_cast<double>(system_frames) / interval; 53 results.system_fps = static_cast<double>(system_frames) / interval;
55 results.game_fps = static_cast<double>(game_frames) / interval; 54 results.game_fps = static_cast<double>(game_frames) / interval;
56 results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / 55 results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
57 static_cast<double>(system_frames); 56 static_cast<double>(system_frames);
58 results.emulation_speed = system_us_per_second / 1'000'000.0; 57 results.emulation_speed = system_us_per_second.count() / 1'000'000.0;
59 58
60 // Reset counters 59 // Reset counters
61 reset_point = now; 60 reset_point = now;
@@ -74,10 +73,10 @@ double PerfStats::GetLastFrameTimeScale() {
74 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; 73 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
75} 74}
76 75
77void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { 76void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) {
78 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher 77 // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
79 // values increase the time needed to recover and limit framerate again after spikes. 78 // values increase the time needed to recover and limit framerate again after spikes.
80 constexpr microseconds MAX_LAG_TIME_US = 25ms; 79 constexpr microseconds MAX_LAG_TIME_US = 25us;
81 80
82 if (!Settings::values.toggle_framelimit) { 81 if (!Settings::values.toggle_framelimit) {
83 return; 82 return;
@@ -85,7 +84,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
85 84
86 auto now = Clock::now(); 85 auto now = Clock::now();
87 86
88 frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); 87 frame_limiting_delta_err += current_system_time_us - previous_system_time_us;
89 frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); 88 frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
90 frame_limiting_delta_err = 89 frame_limiting_delta_err =
91 std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); 90 std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US);
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 362b205c8..6e4619701 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -33,7 +33,7 @@ public:
33 void EndSystemFrame(); 33 void EndSystemFrame();
34 void EndGameFrame(); 34 void EndGameFrame();
35 35
36 Results GetAndResetStats(u64 current_system_time_us); 36 Results GetAndResetStats(std::chrono::microseconds current_system_time_us);
37 37
38 /** 38 /**
39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is 39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is
@@ -47,7 +47,7 @@ private:
47 /// Point when the cumulative counters were reset 47 /// Point when the cumulative counters were reset
48 Clock::time_point reset_point = Clock::now(); 48 Clock::time_point reset_point = Clock::now();
49 /// System time when the cumulative counters were reset 49 /// System time when the cumulative counters were reset
50 u64 reset_point_system_us = 0; 50 std::chrono::microseconds reset_point_system_us{0};
51 51
52 /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset 52 /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset
53 Clock::duration accumulated_frametime = Clock::duration::zero(); 53 Clock::duration accumulated_frametime = Clock::duration::zero();
@@ -68,11 +68,11 @@ class FrameLimiter {
68public: 68public:
69 using Clock = std::chrono::high_resolution_clock; 69 using Clock = std::chrono::high_resolution_clock;
70 70
71 void DoFrameLimiting(u64 current_system_time_us); 71 void DoFrameLimiting(std::chrono::microseconds current_system_time_us);
72 72
73private: 73private:
74 /// Emulated system time (in microseconds) at the last limiter invocation 74 /// Emulated system time (in microseconds) at the last limiter invocation
75 u64 previous_system_time_us = 0; 75 std::chrono::microseconds previous_system_time_us{0};
76 /// Walltime at the last limiter invocation 76 /// Walltime at the last limiter invocation
77 Clock::time_point previous_walltime = Clock::now(); 77 Clock::time_point previous_walltime = Clock::now();
78 78
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 444bcc387..a4623223d 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -2,13 +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 "core/core.h"
5#include "core/gdbstub/gdbstub.h" 6#include "core/gdbstub/gdbstub.h"
6#include "core/hle/service/hid/hid.h" 7#include "core/hle/service/hid/hid.h"
7#include "core/settings.h" 8#include "core/settings.h"
9#include "video_core/renderer_base.h"
8#include "video_core/video_core.h" 10#include "video_core/video_core.h"
9 11
10#include "core/frontend/emu_window.h"
11
12namespace Settings { 12namespace Settings {
13 13
14Values values = {}; 14Values values = {};
@@ -20,9 +20,9 @@ void Apply() {
20 20
21 VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; 21 VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
22 22
23 if (VideoCore::g_emu_window) { 23 auto& system_instance = Core::System::GetInstance();
24 auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); 24 if (system_instance.IsPoweredOn()) {
25 VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); 25 system_instance.Renderer().UpdateCurrentFramebufferLayout();
26 } 26 }
27 27
28 Service::HID::ReloadInputDevices(); 28 Service::HID::ReloadInputDevices();
diff --git a/src/core/settings.h b/src/core/settings.h
index 7150d9755..73dc3061f 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -112,6 +112,8 @@ static const std::array<const char*, NumAnalogs> mapping = {{
112struct Values { 112struct Values {
113 // System 113 // System
114 bool use_docked_mode; 114 bool use_docked_mode;
115 std::string username;
116 int language_index;
115 117
116 // Controls 118 // Controls
117 std::array<std::string, NativeButton::NumButtons> buttons; 119 std::array<std::string, NativeButton::NumButtons> buttons;
@@ -137,6 +139,13 @@ struct Values {
137 139
138 std::string log_filter; 140 std::string log_filter;
139 141
142 bool use_dev_keys;
143
144 // Audio
145 std::string sink_id;
146 std::string audio_device_id;
147 float volume;
148
140 // Debugging 149 // Debugging
141 bool use_gdbstub; 150 bool use_gdbstub;
142 u16 gdbstub_port; 151 u16 gdbstub_port;
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
index 0f0d10f23..525fe6abc 100644
--- a/src/input_common/keyboard.cpp
+++ b/src/input_common/keyboard.cpp
@@ -5,6 +5,7 @@
5#include <atomic> 5#include <atomic>
6#include <list> 6#include <list>
7#include <mutex> 7#include <mutex>
8#include <utility>
8#include "input_common/keyboard.h" 9#include "input_common/keyboard.h"
9 10
10namespace InputCommon { 11namespace InputCommon {
@@ -12,9 +13,9 @@ namespace InputCommon {
12class KeyButton final : public Input::ButtonDevice { 13class KeyButton final : public Input::ButtonDevice {
13public: 14public:
14 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_) 15 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_)
15 : key_button_list(key_button_list_) {} 16 : key_button_list(std::move(key_button_list_)) {}
16 17
17 ~KeyButton(); 18 ~KeyButton() override;
18 19
19 bool GetStatus() const override { 20 bool GetStatus() const override {
20 return status.load(); 21 return status.load();
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index caffe48cb..9570c060e 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -131,7 +131,7 @@ public:
131 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity); 131 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
132 } 132 }
133 133
134 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const { 134 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const override {
135 return device->GetStatus(); 135 return device->GetStatus();
136 } 136 }
137 137
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index 8d117c2d4..d1b960fd7 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -82,7 +82,7 @@ private:
82class SDLButton final : public Input::ButtonDevice { 82class SDLButton final : public Input::ButtonDevice {
83public: 83public:
84 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) 84 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
85 : joystick(joystick_), button(button_) {} 85 : joystick(std::move(joystick_)), button(button_) {}
86 86
87 bool GetStatus() const override { 87 bool GetStatus() const override {
88 return joystick->GetButton(button); 88 return joystick->GetButton(button);
@@ -96,7 +96,7 @@ private:
96class SDLDirectionButton final : public Input::ButtonDevice { 96class SDLDirectionButton final : public Input::ButtonDevice {
97public: 97public:
98 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) 98 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
99 : joystick(joystick_), hat(hat_), direction(direction_) {} 99 : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
100 100
101 bool GetStatus() const override { 101 bool GetStatus() const override {
102 return joystick->GetHatDirection(hat, direction); 102 return joystick->GetHatDirection(hat, direction);
@@ -112,7 +112,7 @@ class SDLAxisButton final : public Input::ButtonDevice {
112public: 112public:
113 explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_, 113 explicit SDLAxisButton(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
114 bool trigger_if_greater_) 114 bool trigger_if_greater_)
115 : joystick(joystick_), axis(axis_), threshold(threshold_), 115 : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
116 trigger_if_greater(trigger_if_greater_) {} 116 trigger_if_greater(trigger_if_greater_) {}
117 117
118 bool GetStatus() const override { 118 bool GetStatus() const override {
@@ -132,7 +132,7 @@ private:
132class SDLAnalog final : public Input::AnalogDevice { 132class SDLAnalog final : public Input::AnalogDevice {
133public: 133public:
134 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_) 134 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_)
135 : joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {} 135 : joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_) {}
136 136
137 std::tuple<float, float> GetStatus() const override { 137 std::tuple<float, float> GetStatus() const override {
138 return joystick->GetAnalog(axis_x, axis_y); 138 return joystick->GetAnalog(axis_x, axis_y);
@@ -314,10 +314,6 @@ namespace Polling {
314 314
315class SDLPoller : public InputCommon::Polling::DevicePoller { 315class SDLPoller : public InputCommon::Polling::DevicePoller {
316public: 316public:
317 SDLPoller() = default;
318
319 ~SDLPoller() = default;
320
321 void Start() override { 317 void Start() override {
322 // SDL joysticks must be opened, otherwise they don't generate events 318 // SDL joysticks must be opened, otherwise they don't generate events
323 SDL_JoystickUpdate(); 319 SDL_JoystickUpdate();
@@ -341,10 +337,6 @@ private:
341 337
342class SDLButtonPoller final : public SDLPoller { 338class SDLButtonPoller final : public SDLPoller {
343public: 339public:
344 SDLButtonPoller() = default;
345
346 ~SDLButtonPoller() = default;
347
348 Common::ParamPackage GetNextInput() override { 340 Common::ParamPackage GetNextInput() override {
349 SDL_Event event; 341 SDL_Event event;
350 while (SDL_PollEvent(&event)) { 342 while (SDL_PollEvent(&event)) {
@@ -364,10 +356,6 @@ public:
364 356
365class SDLAnalogPoller final : public SDLPoller { 357class SDLAnalogPoller final : public SDLPoller {
366public: 358public:
367 SDLAnalogPoller() = default;
368
369 ~SDLAnalogPoller() = default;
370
371 void Start() override { 359 void Start() override {
372 SDLPoller::Start(); 360 SDLPoller::Start();
373 361
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 6a0a62ecc..4d74bb395 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -3,7 +3,6 @@ add_executable(tests
3 core/arm/arm_test_common.cpp 3 core/arm/arm_test_common.cpp
4 core/arm/arm_test_common.h 4 core/arm/arm_test_common.h
5 core/core_timing.cpp 5 core/core_timing.cpp
6 core/memory/memory.cpp
7 glad.cpp 6 glad.cpp
8 tests.cpp 7 tests.cpp
9) 8)
diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp
deleted file mode 100644
index 165496a54..000000000
--- a/src/tests/core/memory/memory.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <catch.hpp>
6#include "core/hle/kernel/memory.h"
7#include "core/hle/kernel/process.h"
8#include "core/memory.h"
9
10TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory][!hide]") {
11 SECTION("these regions should not be mapped on an empty process") {
12 auto process = Kernel::Process::Create("");
13 CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);
14 CHECK(Memory::IsValidVirtualAddress(*process, Memory::HEAP_VADDR) == false);
15 CHECK(Memory::IsValidVirtualAddress(*process, Memory::LINEAR_HEAP_VADDR) == false);
16 CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == false);
17 CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false);
18 CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == false);
19 CHECK(Memory::IsValidVirtualAddress(*process, Memory::TLS_AREA_VADDR) == false);
20 }
21
22 SECTION("CONFIG_MEMORY_VADDR and SHARED_PAGE_VADDR should be valid after mapping them") {
23 auto process = Kernel::Process::Create("");
24 Kernel::MapSharedPages(process->vm_manager);
25 CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == true);
26 CHECK(Memory::IsValidVirtualAddress(*process, Memory::SHARED_PAGE_VADDR) == true);
27 }
28
29 SECTION("special regions should be valid after mapping them") {
30 auto process = Kernel::Process::Create("");
31 SECTION("VRAM") {
32 Kernel::HandleSpecialMapping(process->vm_manager,
33 {Memory::VRAM_VADDR, Memory::VRAM_SIZE, false, false});
34 CHECK(Memory::IsValidVirtualAddress(*process, Memory::VRAM_VADDR) == true);
35 }
36
37 SECTION("IO (Not yet implemented)") {
38 Kernel::HandleSpecialMapping(
39 process->vm_manager, {Memory::IO_AREA_VADDR, Memory::IO_AREA_SIZE, false, false});
40 CHECK_FALSE(Memory::IsValidVirtualAddress(*process, Memory::IO_AREA_VADDR) == true);
41 }
42
43 SECTION("DSP") {
44 Kernel::HandleSpecialMapping(
45 process->vm_manager, {Memory::DSP_RAM_VADDR, Memory::DSP_RAM_SIZE, false, false});
46 CHECK(Memory::IsValidVirtualAddress(*process, Memory::DSP_RAM_VADDR) == true);
47 }
48 }
49
50 SECTION("Unmapping a VAddr should make it invalid") {
51 auto process = Kernel::Process::Create("");
52 Kernel::MapSharedPages(process->vm_manager);
53 process->vm_manager.UnmapRange(Memory::CONFIG_MEMORY_VADDR, Memory::CONFIG_MEMORY_SIZE);
54 CHECK(Memory::IsValidVirtualAddress(*process, Memory::CONFIG_MEMORY_VADDR) == false);
55 }
56}
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 0e205ed72..5c0ae8009 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -19,8 +19,8 @@ namespace Engines {
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(MemoryManager& memory_manager) 22Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
23 : memory_manager(memory_manager), macro_interpreter(*this) {} 23 : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {}
24 24
25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { 25void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
26 auto macro_code = uploaded_macros.find(method); 26 auto macro_code = uploaded_macros.find(method);
@@ -130,7 +130,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
130 break; 130 break;
131 } 131 }
132 132
133 VideoCore::g_renderer->Rasterizer()->NotifyMaxwellRegisterChanged(method); 133 rasterizer.NotifyMaxwellRegisterChanged(method);
134 134
135 if (debug_context) { 135 if (debug_context) {
136 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr); 136 debug_context->OnEvent(Tegra::DebugContext::Event::MaxwellCommandProcessed, nullptr);
@@ -218,7 +218,7 @@ void Maxwell3D::DrawArrays() {
218 } 218 }
219 219
220 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; 220 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
221 VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(is_indexed); 221 rasterizer.AccelerateDrawBatch(is_indexed);
222 222
223 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if 223 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
224 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still - 224 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
@@ -285,8 +285,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
285 285
286 // TODO(Subv): Different data types for separate components are not supported 286 // TODO(Subv): Different data types for separate components are not supported
287 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type); 287 ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
288 // TODO(Subv): Only UNORM formats are supported for now.
289 ASSERT(r_type == Texture::ComponentType::UNORM);
290 288
291 return tic_entry; 289 return tic_entry;
292} 290}
@@ -393,7 +391,7 @@ void Maxwell3D::ProcessClearBuffers() {
393 regs.clear_buffers.R == regs.clear_buffers.B && 391 regs.clear_buffers.R == regs.clear_buffers.B &&
394 regs.clear_buffers.R == regs.clear_buffers.A); 392 regs.clear_buffers.R == regs.clear_buffers.A);
395 393
396 VideoCore::g_renderer->Rasterizer()->Clear(); 394 rasterizer.Clear();
397} 395}
398 396
399} // namespace Engines 397} // namespace Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3c32f1067..4d0ff96a5 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -17,6 +17,10 @@
17#include "video_core/memory_manager.h" 17#include "video_core/memory_manager.h"
18#include "video_core/textures/texture.h" 18#include "video_core/textures/texture.h"
19 19
20namespace VideoCore {
21class RasterizerInterface;
22}
23
20namespace Tegra::Engines { 24namespace Tegra::Engines {
21 25
22#define MAXWELL3D_REG_INDEX(field_name) \ 26#define MAXWELL3D_REG_INDEX(field_name) \
@@ -24,7 +28,7 @@ namespace Tegra::Engines {
24 28
25class Maxwell3D final { 29class Maxwell3D final {
26public: 30public:
27 explicit Maxwell3D(MemoryManager& memory_manager); 31 explicit Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
28 ~Maxwell3D() = default; 32 ~Maxwell3D() = default;
29 33
30 /// Register structure of the Maxwell3D engine. 34 /// Register structure of the Maxwell3D engine.
@@ -818,6 +822,8 @@ public:
818 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; 822 Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const;
819 823
820private: 824private:
825 VideoCore::RasterizerInterface& rasterizer;
826
821 std::unordered_map<u32, std::vector<u32>> uploaded_macros; 827 std::unordered_map<u32, std::vector<u32>> uploaded_macros;
822 828
823 /// Macro method that is currently being executed / being fed parameters. 829 /// Macro method that is currently being executed / being fed parameters.
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 60c49d672..b2a83ce0b 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -7,12 +7,13 @@
7#include "video_core/engines/maxwell_compute.h" 7#include "video_core/engines/maxwell_compute.h"
8#include "video_core/engines/maxwell_dma.h" 8#include "video_core/engines/maxwell_dma.h"
9#include "video_core/gpu.h" 9#include "video_core/gpu.h"
10#include "video_core/rasterizer_interface.h"
10 11
11namespace Tegra { 12namespace Tegra {
12 13
13GPU::GPU() { 14GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
14 memory_manager = std::make_unique<MemoryManager>(); 15 memory_manager = std::make_unique<MemoryManager>();
15 maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager); 16 maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
16 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); 17 fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
17 maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); 18 maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
18 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); 19 maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
@@ -40,6 +41,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
40 case RenderTargetFormat::RGBA8_UNORM: 41 case RenderTargetFormat::RGBA8_UNORM:
41 case RenderTargetFormat::RGB10_A2_UNORM: 42 case RenderTargetFormat::RGB10_A2_UNORM:
42 case RenderTargetFormat::BGRA8_UNORM: 43 case RenderTargetFormat::BGRA8_UNORM:
44 case RenderTargetFormat::R32_FLOAT:
43 return 4; 45 return 4;
44 default: 46 default:
45 UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format)); 47 UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index c464fc6d1..440505c9d 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -11,6 +11,10 @@
11#include "core/hle/service/nvflinger/buffer_queue.h" 11#include "core/hle/service/nvflinger/buffer_queue.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 { 18namespace Tegra {
15 19
16enum class RenderTargetFormat : u32 { 20enum class RenderTargetFormat : u32 {
@@ -29,6 +33,7 @@ enum class RenderTargetFormat : u32 {
29 RG16_UINT = 0xDD, 33 RG16_UINT = 0xDD,
30 RG16_FLOAT = 0xDE, 34 RG16_FLOAT = 0xDE,
31 R11G11B10_FLOAT = 0xE0, 35 R11G11B10_FLOAT = 0xE0,
36 R32_FLOAT = 0xE5,
32 R16_FLOAT = 0xF2, 37 R16_FLOAT = 0xF2,
33 R8_UNORM = 0xF3, 38 R8_UNORM = 0xF3,
34}; 39};
@@ -97,7 +102,7 @@ enum class EngineID {
97 102
98class GPU final { 103class GPU final {
99public: 104public:
100 GPU(); 105 explicit GPU(VideoCore::RasterizerInterface& rasterizer);
101 ~GPU(); 106 ~GPU();
102 107
103 /// Processes a command list stored at the specified address in GPU memory. 108 /// Processes a command list stored at the specified address in GPU memory.
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 44ece01c1..377bd66ab 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -102,11 +102,11 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
102 if (taken) { 102 if (taken) {
103 // Ignore the delay slot if the branch has the annul bit. 103 // Ignore the delay slot if the branch has the annul bit.
104 if (opcode.branch_annul) { 104 if (opcode.branch_annul) {
105 pc = base_address + (opcode.immediate << 2); 105 pc = base_address + opcode.GetBranchTarget();
106 return true; 106 return true;
107 } 107 }
108 108
109 delayed_pc = base_address + (opcode.immediate << 2); 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(code, true);
112 } 112 }
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h
index a71e359d8..7d836b816 100644
--- a/src/video_core/macro_interpreter.h
+++ b/src/video_core/macro_interpreter.h
@@ -91,6 +91,10 @@ private:
91 u32 GetBitfieldMask() const { 91 u32 GetBitfieldMask() const {
92 return (1 << bf_size) - 1; 92 return (1 << bf_size) - 1;
93 } 93 }
94
95 s32 GetBranchTarget() const {
96 return static_cast<s32>(immediate * sizeof(u32));
97 }
94 }; 98 };
95 99
96 union MethodAddress { 100 union MethodAddress {
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index 30075b23c..3ca350243 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -2,14 +2,26 @@
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#include <memory> 5#include <memory>
6#include "core/frontend/emu_window.h"
7#include "video_core/renderer_base.h" 7#include "video_core/renderer_base.h"
8#include "video_core/renderer_opengl/gl_rasterizer.h" 8#include "video_core/renderer_opengl/gl_rasterizer.h"
9#include "video_core/video_core.h" 9
10namespace VideoCore {
11
12RendererBase::RendererBase(EmuWindow& window) : render_window{window} {}
13RendererBase::~RendererBase() = default;
14
15void RendererBase::UpdateCurrentFramebufferLayout() {
16 const Layout::FramebufferLayout& layout = render_window.GetFramebufferLayout();
17
18 render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
19}
10 20
11void RendererBase::RefreshRasterizerSetting() { 21void RendererBase::RefreshRasterizerSetting() {
12 if (rasterizer == nullptr) { 22 if (rasterizer == nullptr) {
13 rasterizer = std::make_unique<RasterizerOpenGL>(); 23 rasterizer = std::make_unique<RasterizerOpenGL>(render_window);
14 } 24 }
15} 25}
26
27} // namespace VideoCore
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 89a960eaf..235de23a1 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -13,28 +13,28 @@
13 13
14class EmuWindow; 14class EmuWindow;
15 15
16namespace VideoCore {
17
16class RendererBase : NonCopyable { 18class RendererBase : NonCopyable {
17public: 19public:
18 /// Used to reference a framebuffer 20 /// Used to reference a framebuffer
19 enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture }; 21 enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture };
20 22
21 virtual ~RendererBase() {} 23 explicit RendererBase(EmuWindow& window);
24 virtual ~RendererBase();
22 25
23 /// Swap buffers (render frame) 26 /// Swap buffers (render frame)
24 virtual void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) = 0; 27 virtual void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) = 0;
25 28
26 /**
27 * Set the emulator window to use for renderer
28 * @param window EmuWindow handle to emulator window to use for rendering
29 */
30 virtual void SetWindow(EmuWindow* window) = 0;
31
32 /// Initialize the renderer 29 /// Initialize the renderer
33 virtual bool Init() = 0; 30 virtual bool Init() = 0;
34 31
35 /// Shutdown the renderer 32 /// Shutdown the renderer
36 virtual void ShutDown() = 0; 33 virtual void ShutDown() = 0;
37 34
35 /// Updates the framebuffer layout of the contained render window handle.
36 void UpdateCurrentFramebufferLayout();
37
38 // Getter/setter functions: 38 // Getter/setter functions:
39 // ------------------------ 39 // ------------------------
40 40
@@ -46,16 +46,21 @@ public:
46 return m_current_frame; 46 return m_current_frame;
47 } 47 }
48 48
49 VideoCore::RasterizerInterface* Rasterizer() const { 49 RasterizerInterface& Rasterizer() {
50 return rasterizer.get(); 50 return *rasterizer;
51 }
52
53 const RasterizerInterface& Rasterizer() const {
54 return *rasterizer;
51 } 55 }
52 56
53 void RefreshRasterizerSetting(); 57 void RefreshRasterizerSetting();
54 58
55protected: 59protected:
56 std::unique_ptr<VideoCore::RasterizerInterface> rasterizer; 60 EmuWindow& render_window; ///< Reference to the render window handle.
61 std::unique_ptr<RasterizerInterface> rasterizer;
57 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer 62 f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
58 int m_current_frame = 0; ///< Current frame, should be set by the renderer 63 int m_current_frame = 0; ///< Current frame, should be set by the renderer
59
60private:
61}; 64};
65
66} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a1c47bae9..c2a931469 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -14,7 +14,6 @@
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15#include "common/math_util.h" 15#include "common/math_util.h"
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/scope_exit.h"
18#include "core/core.h" 17#include "core/core.h"
19#include "core/frontend/emu_window.h" 18#include "core/frontend/emu_window.h"
20#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
@@ -37,7 +36,7 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
37MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); 36MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
38MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); 37MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
39 38
40RasterizerOpenGL::RasterizerOpenGL() { 39RasterizerOpenGL::RasterizerOpenGL(EmuWindow& window) : emu_window{window} {
41 // Create sampler objects 40 // Create sampler objects
42 for (size_t i = 0; i < texture_samplers.size(); ++i) { 41 for (size_t i = 0; i < texture_samplers.size(); ++i) {
43 texture_samplers[i].Create(); 42 texture_samplers[i].Create();
@@ -170,8 +169,14 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
170 ASSERT(buffer.IsEnabled()); 169 ASSERT(buffer.IsEnabled());
171 170
172 glEnableVertexAttribArray(index); 171 glEnableVertexAttribArray(index);
173 glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib), 172 if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
174 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset); 173 attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
174 glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
175 attrib.offset);
176 } else {
177 glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
178 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
179 }
175 glVertexAttribBinding(index, attrib.buffer); 180 glVertexAttribBinding(index, attrib.buffer);
176 } 181 }
177 182
@@ -395,7 +400,7 @@ void RasterizerOpenGL::Clear() {
395 if (clear_mask == 0) 400 if (clear_mask == 0)
396 return; 401 return;
397 402
398 ScopeAcquireGLContext acquire_context; 403 ScopeAcquireGLContext acquire_context{emu_window};
399 404
400 auto [dirty_color_surface, dirty_depth_surface] = 405 auto [dirty_color_surface, dirty_depth_surface] =
401 ConfigureFramebuffers(use_color_fb, use_depth_fb); 406 ConfigureFramebuffers(use_color_fb, use_depth_fb);
@@ -425,7 +430,7 @@ void RasterizerOpenGL::DrawArrays() {
425 MICROPROFILE_SCOPE(OpenGL_Drawing); 430 MICROPROFILE_SCOPE(OpenGL_Drawing);
426 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; 431 const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
427 432
428 ScopeAcquireGLContext acquire_context; 433 ScopeAcquireGLContext acquire_context{emu_window};
429 434
430 auto [dirty_color_surface, dirty_depth_surface] = 435 auto [dirty_color_surface, dirty_depth_surface] =
431 ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0); 436 ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index e150be58f..6d6d85cc1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -21,11 +21,12 @@
21#include "video_core/renderer_opengl/gl_state.h" 21#include "video_core/renderer_opengl/gl_state.h"
22#include "video_core/renderer_opengl/gl_stream_buffer.h" 22#include "video_core/renderer_opengl/gl_stream_buffer.h"
23 23
24class EmuWindow;
24struct ScreenInfo; 25struct ScreenInfo;
25 26
26class RasterizerOpenGL : public VideoCore::RasterizerInterface { 27class RasterizerOpenGL : public VideoCore::RasterizerInterface {
27public: 28public:
28 RasterizerOpenGL(); 29 explicit RasterizerOpenGL(EmuWindow& renderer);
29 ~RasterizerOpenGL() override; 30 ~RasterizerOpenGL() override;
30 31
31 void DrawArrays() override; 32 void DrawArrays() override;
@@ -144,6 +145,8 @@ private:
144 145
145 RasterizerCacheOpenGL res_cache; 146 RasterizerCacheOpenGL res_cache;
146 147
148 EmuWindow& emu_window;
149
147 std::unique_ptr<GLShader::ProgramManager> shader_program_manager; 150 std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
148 OGLVertexArray sw_vao; 151 OGLVertexArray sw_vao;
149 OGLVertexArray hw_vao; 152 OGLVertexArray hw_vao;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index a4d9707cb..257aa9571 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -46,6 +46,8 @@ struct FormatTuple {
46 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); 46 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
47 params.unaligned_height = config.tic.Height(); 47 params.unaligned_height = config.tic.Height();
48 params.size_in_bytes = params.SizeInBytes(); 48 params.size_in_bytes = params.SizeInBytes();
49 params.cache_width = Common::AlignUp(params.width, 16);
50 params.cache_height = Common::AlignUp(params.height, 16);
49 return params; 51 return params;
50} 52}
51 53
@@ -63,6 +65,8 @@ struct FormatTuple {
63 params.height = config.height; 65 params.height = config.height;
64 params.unaligned_height = config.height; 66 params.unaligned_height = config.height;
65 params.size_in_bytes = params.SizeInBytes(); 67 params.size_in_bytes = params.SizeInBytes();
68 params.cache_width = Common::AlignUp(params.width, 16);
69 params.cache_height = Common::AlignUp(params.height, 16);
66 return params; 70 return params;
67} 71}
68 72
@@ -82,6 +86,8 @@ struct FormatTuple {
82 params.height = zeta_height; 86 params.height = zeta_height;
83 params.unaligned_height = zeta_height; 87 params.unaligned_height = zeta_height;
84 params.size_in_bytes = params.SizeInBytes(); 88 params.size_in_bytes = params.SizeInBytes();
89 params.cache_width = Common::AlignUp(params.width, 16);
90 params.cache_height = Common::AlignUp(params.height, 16);
85 return params; 91 return params;
86} 92}
87 93
@@ -118,6 +124,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
118 {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RG16UI 124 {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RG16UI
119 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I 125 {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I
120 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S 126 {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S
127 {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F
121 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 128 {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8
122 129
123 // DepthStencil formats 130 // DepthStencil formats
@@ -218,9 +225,10 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
218 MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::RG16>, 225 MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::RG16>,
219 MortonCopy<true, PixelFormat::RG16F>, MortonCopy<true, PixelFormat::RG16UI>, 226 MortonCopy<true, PixelFormat::RG16F>, MortonCopy<true, PixelFormat::RG16UI>,
220 MortonCopy<true, PixelFormat::RG16I>, MortonCopy<true, PixelFormat::RG16S>, 227 MortonCopy<true, PixelFormat::RG16I>, MortonCopy<true, PixelFormat::RG16S>,
221 MortonCopy<true, PixelFormat::SRGBA8>, MortonCopy<true, PixelFormat::Z24S8>, 228 MortonCopy<true, PixelFormat::RGB32F>, MortonCopy<true, PixelFormat::SRGBA8>,
222 MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>, 229 MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
223 MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z32FS8>, 230 MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>,
231 MortonCopy<true, PixelFormat::Z32FS8>,
224}; 232};
225 233
226static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), 234static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
@@ -253,6 +261,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr),
253 MortonCopy<false, PixelFormat::RG16UI>, 261 MortonCopy<false, PixelFormat::RG16UI>,
254 MortonCopy<false, PixelFormat::RG16I>, 262 MortonCopy<false, PixelFormat::RG16I>,
255 MortonCopy<false, PixelFormat::RG16S>, 263 MortonCopy<false, PixelFormat::RG16S>,
264 MortonCopy<false, PixelFormat::RGB32F>,
256 MortonCopy<false, PixelFormat::SRGBA8>, 265 MortonCopy<false, PixelFormat::SRGBA8>,
257 MortonCopy<false, PixelFormat::Z24S8>, 266 MortonCopy<false, PixelFormat::Z24S8>,
258 MortonCopy<false, PixelFormat::S8Z24>, 267 MortonCopy<false, PixelFormat::S8Z24>,
@@ -677,12 +686,12 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
677 // If use_accurate_framebuffers is enabled, always load from memory 686 // If use_accurate_framebuffers is enabled, always load from memory
678 FlushSurface(surface); 687 FlushSurface(surface);
679 UnregisterSurface(surface); 688 UnregisterSurface(surface);
680 } else if (surface->GetSurfaceParams() != params) { 689 } else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
681 // If surface parameters changed, recreate the surface from the old one
682 return RecreateSurface(surface, params);
683 } else {
684 // Use the cached surface as-is 690 // Use the cached surface as-is
685 return surface; 691 return surface;
692 } else {
693 // If surface parameters changed, recreate the surface from the old one
694 return RecreateSurface(surface, params);
686 } 695 }
687 } 696 }
688 697
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index bf0458b94..0c6652c7a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -9,6 +9,7 @@
9#include <memory> 9#include <memory>
10#include <vector> 10#include <vector>
11#include <boost/icl/interval_map.hpp> 11#include <boost/icl/interval_map.hpp>
12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/math_util.h" 14#include "common/math_util.h"
14#include "video_core/engines/maxwell_3d.h" 15#include "video_core/engines/maxwell_3d.h"
@@ -48,16 +49,17 @@ struct SurfaceParams {
48 RG16UI = 23, 49 RG16UI = 23,
49 RG16I = 24, 50 RG16I = 24,
50 RG16S = 25, 51 RG16S = 25,
51 SRGBA8 = 26, 52 RGB32F = 26,
53 SRGBA8 = 27,
52 54
53 MaxColorFormat, 55 MaxColorFormat,
54 56
55 // DepthStencil formats 57 // DepthStencil formats
56 Z24S8 = 27, 58 Z24S8 = 28,
57 S8Z24 = 28, 59 S8Z24 = 29,
58 Z32F = 29, 60 Z32F = 30,
59 Z16 = 30, 61 Z16 = 31,
60 Z32FS8 = 31, 62 Z32FS8 = 32,
61 63
62 MaxDepthStencilFormat, 64 MaxDepthStencilFormat,
63 65
@@ -121,6 +123,7 @@ struct SurfaceParams {
121 1, // RG16UI 123 1, // RG16UI
122 1, // RG16I 124 1, // RG16I
123 1, // RG16S 125 1, // RG16S
126 1, // RGB32F
124 1, // SRGBA8 127 1, // SRGBA8
125 1, // Z24S8 128 1, // Z24S8
126 1, // S8Z24 129 1, // S8Z24
@@ -164,6 +167,7 @@ struct SurfaceParams {
164 32, // RG16UI 167 32, // RG16UI
165 32, // RG16I 168 32, // RG16I
166 32, // RG16S 169 32, // RG16S
170 96, // RGB32F
167 32, // SRGBA8 171 32, // SRGBA8
168 32, // Z24S8 172 32, // Z24S8
169 32, // S8Z24 173 32, // S8Z24
@@ -200,8 +204,9 @@ struct SurfaceParams {
200 204
201 static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { 205 static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) {
202 switch (format) { 206 switch (format) {
207 // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the
208 // gamma.
203 case Tegra::RenderTargetFormat::RGBA8_SRGB: 209 case Tegra::RenderTargetFormat::RGBA8_SRGB:
204 return PixelFormat::SRGBA8;
205 case Tegra::RenderTargetFormat::RGBA8_UNORM: 210 case Tegra::RenderTargetFormat::RGBA8_UNORM:
206 return PixelFormat::ABGR8; 211 return PixelFormat::ABGR8;
207 case Tegra::RenderTargetFormat::BGRA8_UNORM: 212 case Tegra::RenderTargetFormat::BGRA8_UNORM:
@@ -232,6 +237,8 @@ struct SurfaceParams {
232 return PixelFormat::RG16S; 237 return PixelFormat::RG16S;
233 case Tegra::RenderTargetFormat::R16_FLOAT: 238 case Tegra::RenderTargetFormat::R16_FLOAT:
234 return PixelFormat::R16F; 239 return PixelFormat::R16F;
240 case Tegra::RenderTargetFormat::R32_FLOAT:
241 return PixelFormat::R32F;
235 default: 242 default:
236 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 243 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
237 UNREACHABLE(); 244 UNREACHABLE();
@@ -270,6 +277,8 @@ struct SurfaceParams {
270 UNREACHABLE(); 277 UNREACHABLE();
271 case Tegra::Texture::TextureFormat::R32_G32: 278 case Tegra::Texture::TextureFormat::R32_G32:
272 return PixelFormat::RG32F; 279 return PixelFormat::RG32F;
280 case Tegra::Texture::TextureFormat::R32_G32_B32:
281 return PixelFormat::RGB32F;
273 case Tegra::Texture::TextureFormat::R16: 282 case Tegra::Texture::TextureFormat::R16:
274 switch (component_type) { 283 switch (component_type) {
275 case Tegra::Texture::ComponentType::FLOAT: 284 case Tegra::Texture::ComponentType::FLOAT:
@@ -361,6 +370,8 @@ struct SurfaceParams {
361 return Tegra::Texture::TextureFormat::A8R8G8B8; 370 return Tegra::Texture::TextureFormat::A8R8G8B8;
362 case PixelFormat::RGBA32F: 371 case PixelFormat::RGBA32F:
363 return Tegra::Texture::TextureFormat::R32_G32_B32_A32; 372 return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
373 case PixelFormat::RGB32F:
374 return Tegra::Texture::TextureFormat::R32_G32_B32;
364 case PixelFormat::RG32F: 375 case PixelFormat::RG32F:
365 return Tegra::Texture::TextureFormat::R32_G32; 376 return Tegra::Texture::TextureFormat::R32_G32;
366 case PixelFormat::R32F: 377 case PixelFormat::R32F:
@@ -439,6 +450,7 @@ struct SurfaceParams {
439 case Tegra::RenderTargetFormat::RG32_FLOAT: 450 case Tegra::RenderTargetFormat::RG32_FLOAT:
440 case Tegra::RenderTargetFormat::RG16_FLOAT: 451 case Tegra::RenderTargetFormat::RG16_FLOAT:
441 case Tegra::RenderTargetFormat::R16_FLOAT: 452 case Tegra::RenderTargetFormat::R16_FLOAT:
453 case Tegra::RenderTargetFormat::R32_FLOAT:
442 return ComponentType::Float; 454 return ComponentType::Float;
443 case Tegra::RenderTargetFormat::RGBA32_UINT: 455 case Tegra::RenderTargetFormat::RGBA32_UINT:
444 case Tegra::RenderTargetFormat::RG16_UINT: 456 case Tegra::RenderTargetFormat::RG16_UINT:
@@ -536,6 +548,12 @@ struct SurfaceParams {
536 return !operator==(other); 548 return !operator==(other);
537 } 549 }
538 550
551 /// Checks if surfaces are compatible for caching
552 bool IsCompatibleSurface(const SurfaceParams& other) const {
553 return std::tie(pixel_format, type, cache_width, cache_height) ==
554 std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height);
555 }
556
539 Tegra::GPUVAddr addr; 557 Tegra::GPUVAddr addr;
540 bool is_tiled; 558 bool is_tiled;
541 u32 block_height; 559 u32 block_height;
@@ -546,6 +564,10 @@ struct SurfaceParams {
546 u32 height; 564 u32 height;
547 u32 unaligned_height; 565 u32 unaligned_height;
548 size_t size_in_bytes; 566 size_t size_in_bytes;
567
568 // Parameters used for caching only
569 u32 cache_width;
570 u32 cache_height;
549}; 571};
550 572
551class CachedSurface final { 573class CachedSurface final {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index acf067050..e3217db81 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -412,7 +412,6 @@ public:
412 } 412 }
413 declarations.AddNewLine(); 413 declarations.AddNewLine();
414 414
415 unsigned const_buffer_layout = 0;
416 for (const auto& entry : GetConstBuffersDeclarations()) { 415 for (const auto& entry : GetConstBuffersDeclarations()) {
417 declarations.AddLine("layout(std140) uniform " + entry.GetName()); 416 declarations.AddLine("layout(std140) uniform " + entry.GetName());
418 declarations.AddLine('{'); 417 declarations.AddLine('{');
@@ -420,7 +419,6 @@ public:
420 "[MAX_CONSTBUFFER_ELEMENTS];"); 419 "[MAX_CONSTBUFFER_ELEMENTS];");
421 declarations.AddLine("};"); 420 declarations.AddLine("};");
422 declarations.AddNewLine(); 421 declarations.AddNewLine();
423 ++const_buffer_layout;
424 } 422 }
425 declarations.AddNewLine(); 423 declarations.AddNewLine();
426 424
@@ -768,13 +766,16 @@ private:
768 // goes into gpr28+0 and gpr28+1 766 // goes into gpr28+0 and gpr28+1
769 size_t texs_offset{}; 767 size_t texs_offset{};
770 768
769 size_t src_elem{};
771 for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) { 770 for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
771 size_t dest_elem{};
772 for (unsigned elem = 0; elem < 2; ++elem) { 772 for (unsigned elem = 0; elem < 2; ++elem) {
773 if (!instr.texs.IsComponentEnabled(elem)) { 773 if (!instr.texs.IsComponentEnabled(src_elem++)) {
774 // Skip disabled components 774 // Skip disabled components
775 continue; 775 continue;
776 } 776 }
777 regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem); 777 regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
778 dest_elem++);
778 } 779 }
779 780
780 if (!instr.texs.HasTwoDestinations()) { 781 if (!instr.texs.HasTwoDestinations()) {
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index e81fcbbc4..415d42fda 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -13,15 +13,16 @@ namespace Impl {
13static void SetShaderUniformBlockBinding(GLuint shader, const char* name, 13static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
14 Maxwell3D::Regs::ShaderStage binding, 14 Maxwell3D::Regs::ShaderStage binding,
15 size_t expected_size) { 15 size_t expected_size) {
16 GLuint ub_index = glGetUniformBlockIndex(shader, name); 16 const GLuint ub_index = glGetUniformBlockIndex(shader, name);
17 if (ub_index != GL_INVALID_INDEX) { 17 if (ub_index == GL_INVALID_INDEX) {
18 GLint ub_size = 0; 18 return;
19 glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size);
20 ASSERT_MSG(ub_size == expected_size,
21 "Uniform block size did not match! Got {}, expected {}",
22 static_cast<int>(ub_size), expected_size);
23 glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding));
24 } 19 }
20
21 GLint ub_size = 0;
22 glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size);
23 ASSERT_MSG(static_cast<size_t>(ub_size) == expected_size,
24 "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size);
25 glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding));
25} 26}
26 27
27void SetShaderUniformBlockBindings(GLuint shader) { 28void SetShaderUniformBlockBindings(GLuint shader) {
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index e29d551e1..716933a0b 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -105,20 +105,20 @@ public:
105 } 105 }
106 106
107 ShaderEntries UseProgrammableVertexShader(const MaxwellVSConfig& config, 107 ShaderEntries UseProgrammableVertexShader(const MaxwellVSConfig& config,
108 const ShaderSetup setup) { 108 const ShaderSetup& setup) {
109 ShaderEntries result; 109 ShaderEntries result;
110 std::tie(current.vs, result) = vertex_shaders.Get(config, setup); 110 std::tie(current.vs, result) = vertex_shaders.Get(config, setup);
111 return result; 111 return result;
112 } 112 }
113 113
114 ShaderEntries UseProgrammableFragmentShader(const MaxwellFSConfig& config, 114 ShaderEntries UseProgrammableFragmentShader(const MaxwellFSConfig& config,
115 const ShaderSetup setup) { 115 const ShaderSetup& setup) {
116 ShaderEntries result; 116 ShaderEntries result;
117 std::tie(current.fs, result) = fragment_shaders.Get(config, setup); 117 std::tie(current.fs, result) = fragment_shaders.Get(config, setup);
118 return result; 118 return result;
119 } 119 }
120 120
121 GLuint GetCurrentProgramStage(Maxwell3D::Regs::ShaderStage stage) { 121 GLuint GetCurrentProgramStage(Maxwell3D::Regs::ShaderStage stage) const {
122 switch (stage) { 122 switch (stage) {
123 case Maxwell3D::Regs::ShaderStage::Vertex: 123 case Maxwell3D::Regs::ShaderStage::Vertex:
124 return current.vs; 124 return current.vs;
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 3398d7c04..24b1d956b 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -82,7 +82,7 @@ public:
82 GLenum logic_op; // GL_LOGIC_OP_MODE 82 GLenum logic_op; // GL_LOGIC_OP_MODE
83 83
84 // 3 texture units - one for each that is used in PICA fragment shader emulation 84 // 3 texture units - one for each that is used in PICA fragment shader emulation
85 struct { 85 struct TextureUnit {
86 GLuint texture_2d; // GL_TEXTURE_BINDING_2D 86 GLuint texture_2d; // GL_TEXTURE_BINDING_2D
87 GLuint sampler; // GL_SAMPLER_BINDING 87 GLuint sampler; // GL_SAMPLER_BINDING
88 struct { 88 struct {
@@ -104,7 +104,8 @@ public:
104 Unbind(); 104 Unbind();
105 sampler = 0; 105 sampler = 0;
106 } 106 }
107 } texture_units[32]; 107 };
108 std::array<TextureUnit, 32> texture_units;
108 109
109 struct { 110 struct {
110 GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING 111 GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 7810b9147..bf9131193 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -92,23 +92,23 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
92 return matrix; 92 return matrix;
93} 93}
94 94
95ScopeAcquireGLContext::ScopeAcquireGLContext() { 95ScopeAcquireGLContext::ScopeAcquireGLContext(EmuWindow& emu_window_) : emu_window{emu_window_} {
96 if (Settings::values.use_multi_core) { 96 if (Settings::values.use_multi_core) {
97 VideoCore::g_emu_window->MakeCurrent(); 97 emu_window.MakeCurrent();
98 } 98 }
99} 99}
100ScopeAcquireGLContext::~ScopeAcquireGLContext() { 100ScopeAcquireGLContext::~ScopeAcquireGLContext() {
101 if (Settings::values.use_multi_core) { 101 if (Settings::values.use_multi_core) {
102 VideoCore::g_emu_window->DoneCurrent(); 102 emu_window.DoneCurrent();
103 } 103 }
104} 104}
105 105
106RendererOpenGL::RendererOpenGL() = default; 106RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {}
107RendererOpenGL::~RendererOpenGL() = default; 107RendererOpenGL::~RendererOpenGL() = default;
108 108
109/// Swap buffers (render frame) 109/// Swap buffers (render frame)
110void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { 110void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) {
111 ScopeAcquireGLContext acquire_context; 111 ScopeAcquireGLContext acquire_context{render_window};
112 112
113 Core::System::GetInstance().perf_stats.EndSystemFrame(); 113 Core::System::GetInstance().perf_stats.EndSystemFrame();
114 114
@@ -130,10 +130,10 @@ void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&
130 // Load the framebuffer from memory, draw it to the screen, and swap buffers 130 // Load the framebuffer from memory, draw it to the screen, and swap buffers
131 LoadFBToScreenInfo(*framebuffer, screen_info); 131 LoadFBToScreenInfo(*framebuffer, screen_info);
132 DrawScreen(); 132 DrawScreen();
133 render_window->SwapBuffers(); 133 render_window.SwapBuffers();
134 } 134 }
135 135
136 render_window->PollEvents(); 136 render_window.PollEvents();
137 137
138 Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs()); 138 Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs());
139 Core::System::GetInstance().perf_stats.BeginSystemFrame(); 139 Core::System::GetInstance().perf_stats.BeginSystemFrame();
@@ -160,8 +160,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
160 // only allows rows to have a memory alignement of 4. 160 // only allows rows to have a memory alignement of 4.
161 ASSERT(framebuffer.stride % 4 == 0); 161 ASSERT(framebuffer.stride % 4 == 0);
162 162
163 if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride, 163 if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride,
164 screen_info)) { 164 screen_info)) {
165 // Reset the screen info's display texture to its own permanent texture 165 // Reset the screen info's display texture to its own permanent texture
166 screen_info.display_texture = screen_info.texture.resource.handle; 166 screen_info.display_texture = screen_info.texture.resource.handle;
167 167
@@ -356,7 +356,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
356 * Draws the emulated screens to the emulator window. 356 * Draws the emulated screens to the emulator window.
357 */ 357 */
358void RendererOpenGL::DrawScreen() { 358void RendererOpenGL::DrawScreen() {
359 const auto& layout = render_window->GetFramebufferLayout(); 359 const auto& layout = render_window.GetFramebufferLayout();
360 const auto& screen = layout.screen; 360 const auto& screen = layout.screen;
361 361
362 glViewport(0, 0, layout.width, layout.height); 362 glViewport(0, 0, layout.width, layout.height);
@@ -380,14 +380,6 @@ void RendererOpenGL::DrawScreen() {
380/// Updates the framerate 380/// Updates the framerate
381void RendererOpenGL::UpdateFramerate() {} 381void RendererOpenGL::UpdateFramerate() {}
382 382
383/**
384 * Set the emulator window to use for renderer
385 * @param window EmuWindow handle to emulator window to use for rendering
386 */
387void RendererOpenGL::SetWindow(EmuWindow* window) {
388 render_window = window;
389}
390
391static const char* GetSource(GLenum source) { 383static const char* GetSource(GLenum source) {
392#define RET(s) \ 384#define RET(s) \
393 case GL_DEBUG_SOURCE_##s: \ 385 case GL_DEBUG_SOURCE_##s: \
@@ -445,7 +437,7 @@ static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum
445 437
446/// Initialize the renderer 438/// Initialize the renderer
447bool RendererOpenGL::Init() { 439bool RendererOpenGL::Init() {
448 ScopeAcquireGLContext acquire_context; 440 ScopeAcquireGLContext acquire_context{render_window};
449 441
450 if (GLAD_GL_KHR_debug) { 442 if (GLAD_GL_KHR_debug) {
451 glEnable(GL_DEBUG_OUTPUT); 443 glEnable(GL_DEBUG_OUTPUT);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 59d92a3dc..428afa3b7 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -34,24 +34,21 @@ struct ScreenInfo {
34/// Helper class to acquire/release OpenGL context within a given scope 34/// Helper class to acquire/release OpenGL context within a given scope
35class ScopeAcquireGLContext : NonCopyable { 35class ScopeAcquireGLContext : NonCopyable {
36public: 36public:
37 ScopeAcquireGLContext(); 37 explicit ScopeAcquireGLContext(EmuWindow& window);
38 ~ScopeAcquireGLContext(); 38 ~ScopeAcquireGLContext();
39
40private:
41 EmuWindow& emu_window;
39}; 42};
40 43
41class RendererOpenGL : public RendererBase { 44class RendererOpenGL : public VideoCore::RendererBase {
42public: 45public:
43 RendererOpenGL(); 46 explicit RendererOpenGL(EmuWindow& window);
44 ~RendererOpenGL() override; 47 ~RendererOpenGL() override;
45 48
46 /// Swap buffers (render frame) 49 /// Swap buffers (render frame)
47 void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override; 50 void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override;
48 51
49 /**
50 * Set the emulator window to use for renderer
51 * @param window EmuWindow handle to emulator window to use for rendering
52 */
53 void SetWindow(EmuWindow* window) override;
54
55 /// Initialize the renderer 52 /// Initialize the renderer
56 bool Init() override; 53 bool Init() override;
57 54
@@ -72,8 +69,6 @@ private:
72 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, 69 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
73 const TextureInfo& texture); 70 const TextureInfo& texture);
74 71
75 EmuWindow* render_window; ///< Handle to render window
76
77 OpenGLState state; 72 OpenGLState state;
78 73
79 // OpenGL object IDs 74 // OpenGL object IDs
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index d794f8402..65db84ad3 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -57,6 +57,8 @@ u32 BytesPerPixel(TextureFormat format) {
57 case TextureFormat::BC7U: 57 case TextureFormat::BC7U:
58 // In this case a 'pixel' actually refers to a 4x4 tile. 58 // In this case a 'pixel' actually refers to a 4x4 tile.
59 return 16; 59 return 16;
60 case TextureFormat::R32_G32_B32:
61 return 12;
60 case TextureFormat::ASTC_2D_4X4: 62 case TextureFormat::ASTC_2D_4X4:
61 case TextureFormat::A8R8G8B8: 63 case TextureFormat::A8R8G8B8:
62 case TextureFormat::A2B10G10R10: 64 case TextureFormat::A2B10G10R10:
@@ -131,6 +133,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
131 case TextureFormat::R16_G16: 133 case TextureFormat::R16_G16:
132 case TextureFormat::BF10GF11RF11: 134 case TextureFormat::BF10GF11RF11:
133 case TextureFormat::ASTC_2D_4X4: 135 case TextureFormat::ASTC_2D_4X4:
136 case TextureFormat::R32_G32_B32:
134 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, 137 CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
135 unswizzled_data.data(), true, block_height); 138 unswizzled_data.data(), true, block_height);
136 break; 139 break;
@@ -190,6 +193,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
190 case TextureFormat::R32: 193 case TextureFormat::R32:
191 case TextureFormat::R16: 194 case TextureFormat::R16:
192 case TextureFormat::R16_G16: 195 case TextureFormat::R16_G16:
196 case TextureFormat::R32_G32_B32:
193 // TODO(Subv): For the time being just forward the same data without any decoding. 197 // TODO(Subv): For the time being just forward the same data without any decoding.
194 rgba_data = texture_data; 198 rgba_data = texture_data;
195 break; 199 break;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 289140f31..5085ef96b 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -3,40 +3,16 @@
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 "common/logging/log.h"
7#include "video_core/renderer_base.h" 6#include "video_core/renderer_base.h"
8#include "video_core/renderer_opengl/renderer_opengl.h" 7#include "video_core/renderer_opengl/renderer_opengl.h"
9#include "video_core/video_core.h" 8#include "video_core/video_core.h"
10 9
11////////////////////////////////////////////////////////////////////////////////////////////////////
12// Video Core namespace
13
14namespace VideoCore { 10namespace VideoCore {
15 11
16EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
17std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
18
19std::atomic<bool> g_toggle_framelimit_enabled; 12std::atomic<bool> g_toggle_framelimit_enabled;
20 13
21/// Initialize the video core 14std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) {
22bool Init(EmuWindow* emu_window) { 15 return std::make_unique<RendererOpenGL>(emu_window);
23 g_emu_window = emu_window;
24 g_renderer = std::make_unique<RendererOpenGL>();
25 g_renderer->SetWindow(g_emu_window);
26 if (g_renderer->Init()) {
27 LOG_DEBUG(Render, "initialized OK");
28 } else {
29 LOG_CRITICAL(Render, "initialization failed !");
30 return false;
31 }
32 return true;
33}
34
35/// Shutdown the video core
36void Shutdown() {
37 g_renderer.reset();
38
39 LOG_DEBUG(Render, "shutdown OK");
40} 16}
41 17
42} // namespace VideoCore 18} // namespace VideoCore
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 37da62436..7c01c0b8d 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -8,29 +8,23 @@
8#include <memory> 8#include <memory>
9 9
10class EmuWindow; 10class EmuWindow;
11class RendererBase;
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// Video Core namespace
15 11
16namespace VideoCore { 12namespace VideoCore {
17 13
18enum class Renderer { Software, OpenGL }; 14class RendererBase;
19 15
20extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin 16enum class Renderer { Software, OpenGL };
21extern EmuWindow* g_emu_window; ///< Emu window
22 17
23// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from 18// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
24// qt ui) 19// qt ui)
25extern std::atomic<bool> g_toggle_framelimit_enabled; 20extern std::atomic<bool> g_toggle_framelimit_enabled;
26 21
27/// Start the video core 22/**
28void Start(); 23 * Creates a renderer instance.
29 24 *
30/// Initialize the video core 25 * @note The returned renderer instance is simply allocated. Its Init()
31bool Init(EmuWindow* emu_window); 26 * function still needs to be called to fully complete its setup.
32 27 */
33/// Shutdown the video core 28std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window);
34void Shutdown();
35 29
36} // namespace VideoCore 30} // namespace VideoCore
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 7de919a8e..475556806 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -11,6 +11,8 @@ add_executable(yuzu
11 bootmanager.h 11 bootmanager.h
12 configuration/config.cpp 12 configuration/config.cpp
13 configuration/config.h 13 configuration/config.h
14 configuration/configure_audio.cpp
15 configuration/configure_audio.h
14 configuration/configure_debug.cpp 16 configuration/configure_debug.cpp
15 configuration/configure_debug.h 17 configuration/configure_debug.h
16 configuration/configure_dialog.cpp 18 configuration/configure_dialog.cpp
@@ -55,6 +57,7 @@ add_executable(yuzu
55set(UIS 57set(UIS
56 aboutdialog.ui 58 aboutdialog.ui
57 configuration/configure.ui 59 configuration/configure.ui
60 configuration/configure_audio.ui
58 configuration/configure_debug.ui 61 configuration/configure_debug.ui
59 configuration/configure_general.ui 62 configuration/configure_general.ui
60 configuration/configure_graphics.ui 63 configuration/configure_graphics.ui
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index d6647eeea..a81ad2888 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -10,8 +10,9 @@
10AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { 10AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) {
11 ui->setupUi(this); 11 ui->setupUi(this);
12 ui->labelLogo->setPixmap(QIcon::fromTheme("yuzu").pixmap(200)); 12 ui->labelLogo->setPixmap(QIcon::fromTheme("yuzu").pixmap(200));
13 ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( 13 ui->labelBuildInfo->setText(
14 Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); 14 ui->labelBuildInfo->text().arg(Common::g_build_name, Common::g_scm_branch,
15 Common::g_scm_desc, QString(Common::g_build_date).left(10)));
15} 16}
16 17
17AboutDialog::~AboutDialog() {} 18AboutDialog::~AboutDialog() = default;
diff --git a/src/yuzu/about_dialog.h b/src/yuzu/about_dialog.h
index 2eb6e28f5..18e8c11a7 100644
--- a/src/yuzu/about_dialog.h
+++ b/src/yuzu/about_dialog.h
@@ -16,7 +16,7 @@ class AboutDialog : public QDialog {
16 16
17public: 17public:
18 explicit AboutDialog(QWidget* parent); 18 explicit AboutDialog(QWidget* parent);
19 ~AboutDialog(); 19 ~AboutDialog() override;
20 20
21private: 21private:
22 std::unique_ptr<Ui::AboutDialog> ui; 22 std::unique_ptr<Ui::AboutDialog> ui;
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index 2680480cc..f122ba39d 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -70,7 +70,7 @@
70 </sizepolicy> 70 </sizepolicy>
71 </property> 71 </property>
72 <property name="text"> 72 <property name="text">
73 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 73 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
74 </property> 74 </property>
75 </widget> 75 </widget>
76 </item> 76 </item>
@@ -115,7 +115,7 @@ p, li { white-space: pre-wrap; }
115 <item> 115 <item>
116 <widget class="QLabel" name="labelLinks"> 116 <widget class="QLabel" name="labelLinks">
117 <property name="text"> 117 <property name="text">
118 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 118 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
119 </property> 119 </property>
120 <property name="openExternalLinks"> 120 <property name="openExternalLinks">
121 <bool>true</bool> 121 <bool>true</bool>
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 130bc613b..d0f990c64 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -106,7 +106,7 @@ class GRenderWindow : public QWidget, public EmuWindow {
106 106
107public: 107public:
108 GRenderWindow(QWidget* parent, EmuThread* emu_thread); 108 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
109 ~GRenderWindow(); 109 ~GRenderWindow() override;
110 110
111 // EmuWindow implementation 111 // EmuWindow implementation
112 void SwapBuffers() override; 112 void SwapBuffers() override;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 98969fe10..bf469ee73 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -92,16 +92,26 @@ void Config::ReadValues() {
92 Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat(); 92 Settings::values.bg_blue = qt_config->value("bg_blue", 0.0).toFloat();
93 qt_config->endGroup(); 93 qt_config->endGroup();
94 94
95 qt_config->beginGroup("Audio");
96 Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
97 Settings::values.audio_device_id =
98 qt_config->value("output_device", "auto").toString().toStdString();
99 Settings::values.volume = qt_config->value("volume", 1).toFloat();
100 qt_config->endGroup();
101
95 qt_config->beginGroup("Data Storage"); 102 qt_config->beginGroup("Data Storage");
96 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); 103 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
97 qt_config->endGroup(); 104 qt_config->endGroup();
98 105
99 qt_config->beginGroup("System"); 106 qt_config->beginGroup("System");
100 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); 107 Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
108 Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString();
109 Settings::values.language_index = qt_config->value("language_index", 1).toInt();
101 qt_config->endGroup(); 110 qt_config->endGroup();
102 111
103 qt_config->beginGroup("Miscellaneous"); 112 qt_config->beginGroup("Miscellaneous");
104 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString(); 113 Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
114 Settings::values.use_dev_keys = qt_config->value("use_dev_keys", false).toBool();
105 qt_config->endGroup(); 115 qt_config->endGroup();
106 116
107 qt_config->beginGroup("Debugging"); 117 qt_config->beginGroup("Debugging");
@@ -195,16 +205,25 @@ void Config::SaveValues() {
195 qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); 205 qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
196 qt_config->endGroup(); 206 qt_config->endGroup();
197 207
208 qt_config->beginGroup("Audio");
209 qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id));
210 qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id));
211 qt_config->setValue("volume", Settings::values.volume);
212 qt_config->endGroup();
213
198 qt_config->beginGroup("Data Storage"); 214 qt_config->beginGroup("Data Storage");
199 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); 215 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
200 qt_config->endGroup(); 216 qt_config->endGroup();
201 217
202 qt_config->beginGroup("System"); 218 qt_config->beginGroup("System");
203 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); 219 qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
220 qt_config->setValue("username", QString::fromStdString(Settings::values.username));
221 qt_config->setValue("language_index", Settings::values.language_index);
204 qt_config->endGroup(); 222 qt_config->endGroup();
205 223
206 qt_config->beginGroup("Miscellaneous"); 224 qt_config->beginGroup("Miscellaneous");
207 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter)); 225 qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
226 qt_config->setValue("use_dev_keys", Settings::values.use_dev_keys);
208 qt_config->endGroup(); 227 qt_config->endGroup();
209 228
210 qt_config->beginGroup("Debugging"); 229 qt_config->beginGroup("Debugging");
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index c5303851c..c8e0b88af 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -34,11 +34,16 @@
34 <string>Input</string> 34 <string>Input</string>
35 </attribute> 35 </attribute>
36 </widget> 36 </widget>
37 <widget class="ConfigureGraphics" name="graphicsTab"> 37 <widget class="ConfigureGraphics" name="graphicsTab">
38 <attribute name="title"> 38 <attribute name="title">
39 <string>Graphics</string> 39 <string>Graphics</string>
40 </attribute> 40 </attribute>
41 </widget> 41 </widget>
42 <widget class="ConfigureAudio" name="audioTab">
43 <attribute name="title">
44 <string>Audio</string>
45 </attribute>
46 </widget>
42 <widget class="ConfigureDebug" name="debugTab"> 47 <widget class="ConfigureDebug" name="debugTab">
43 <attribute name="title"> 48 <attribute name="title">
44 <string>Debug</string> 49 <string>Debug</string>
@@ -69,6 +74,12 @@
69 <container>1</container> 74 <container>1</container>
70 </customwidget> 75 </customwidget>
71 <customwidget> 76 <customwidget>
77 <class>ConfigureAudio</class>
78 <extends>QWidget</extends>
79 <header>configuration/configure_audio.h</header>
80 <container>1</container>
81 </customwidget>
82 <customwidget>
72 <class>ConfigureDebug</class> 83 <class>ConfigureDebug</class>
73 <extends>QWidget</extends> 84 <extends>QWidget</extends>
74 <header>configuration/configure_debug.h</header> 85 <header>configuration/configure_debug.h</header>
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
new file mode 100644
index 000000000..fbb813f6c
--- /dev/null
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -0,0 +1,90 @@
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 <memory>
6
7#include "audio_core/sink.h"
8#include "audio_core/sink_details.h"
9#include "core/core.h"
10#include "core/settings.h"
11#include "ui_configure_audio.h"
12#include "yuzu/configuration/configure_audio.h"
13
14ConfigureAudio::ConfigureAudio(QWidget* parent)
15 : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
16 ui->setupUi(this);
17
18 ui->output_sink_combo_box->clear();
19 ui->output_sink_combo_box->addItem("auto");
20 for (const auto& sink_detail : AudioCore::g_sink_details) {
21 ui->output_sink_combo_box->addItem(sink_detail.id);
22 }
23
24 connect(ui->volume_slider, &QSlider::valueChanged, [this] {
25 ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition()));
26 });
27
28 this->setConfiguration();
29 connect(ui->output_sink_combo_box,
30 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
31 &ConfigureAudio::updateAudioDevices);
32
33 ui->output_sink_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
34 ui->audio_device_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
35}
36
37ConfigureAudio::~ConfigureAudio() = default;
38
39void ConfigureAudio::setConfiguration() {
40 int new_sink_index = 0;
41 for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
42 if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) {
43 new_sink_index = index;
44 break;
45 }
46 }
47 ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
48
49 // The device list cannot be pre-populated (nor listed) until the output sink is known.
50 updateAudioDevices(new_sink_index);
51
52 int new_device_index = -1;
53 for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
54 if (ui->audio_device_combo_box->itemText(index).toStdString() ==
55 Settings::values.audio_device_id) {
56 new_device_index = index;
57 break;
58 }
59 }
60 ui->audio_device_combo_box->setCurrentIndex(new_device_index);
61
62 ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
63 ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition()));
64}
65
66void ConfigureAudio::applyConfiguration() {
67 Settings::values.sink_id =
68 ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
69 .toStdString();
70 Settings::values.audio_device_id =
71 ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
72 .toStdString();
73 Settings::values.volume =
74 static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
75}
76
77void ConfigureAudio::updateAudioDevices(int sink_index) {
78 ui->audio_device_combo_box->clear();
79 ui->audio_device_combo_box->addItem(AudioCore::auto_device_name);
80
81 std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
82 std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices();
83 for (const auto& device : device_list) {
84 ui->audio_device_combo_box->addItem(device.c_str());
85 }
86}
87
88void ConfigureAudio::retranslateUi() {
89 ui->retranslateUi(this);
90}
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h
new file mode 100644
index 000000000..4f0af4163
--- /dev/null
+++ b/src/yuzu/configuration/configure_audio.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 <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureAudio;
12}
13
14class ConfigureAudio : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureAudio(QWidget* parent = nullptr);
19 ~ConfigureAudio();
20
21 void applyConfiguration();
22 void retranslateUi();
23
24public slots:
25 void updateAudioDevices(int sink_index);
26
27private:
28 void setConfiguration();
29
30 std::unique_ptr<Ui::ConfigureAudio> ui;
31};
diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui
new file mode 100644
index 000000000..ef67890dc
--- /dev/null
+++ b/src/yuzu/configuration/configure_audio.ui
@@ -0,0 +1,130 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureAudio</class>
4 <widget class="QWidget" name="ConfigureAudio">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>188</width>
10 <height>246</height>
11 </rect>
12 </property>
13 <layout class="QVBoxLayout">
14 <item>
15 <widget class="QGroupBox" name="groupBox">
16 <property name="title">
17 <string>Audio</string>
18 </property>
19 <layout class="QVBoxLayout">
20 <item>
21 <layout class="QHBoxLayout">
22 <item>
23 <widget class="QLabel" name="label">
24 <property name="text">
25 <string>Output Engine:</string>
26 </property>
27 </widget>
28 </item>
29 <item>
30 <widget class="QComboBox" name="output_sink_combo_box"/>
31 </item>
32 </layout>
33 </item>
34 <item>
35 <layout class="QHBoxLayout">
36 <item>
37 <widget class="QLabel" name="label">
38 <property name="text">
39 <string>Audio Device:</string>
40 </property>
41 </widget>
42 </item>
43 <item>
44 <widget class="QComboBox" name="audio_device_combo_box"/>
45 </item>
46 </layout>
47 </item>
48 <item>
49 <layout class="QHBoxLayout" name="horizontalLayout_2">
50 <property name="topMargin">
51 <number>0</number>
52 </property>
53 <item>
54 <widget class="QLabel" name="label">
55 <property name="text">
56 <string>Volume:</string>
57 </property>
58 </widget>
59 </item>
60 <item>
61 <spacer name="horizontalSpacer">
62 <property name="orientation">
63 <enum>Qt::Horizontal</enum>
64 </property>
65 <property name="sizeHint" stdset="0">
66 <size>
67 <width>40</width>
68 <height>20</height>
69 </size>
70 </property>
71 </spacer>
72 </item>
73 <item>
74 <widget class="QSlider" name="volume_slider">
75 <property name="sizePolicy">
76 <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
77 <horstretch>0</horstretch>
78 <verstretch>0</verstretch>
79 </sizepolicy>
80 </property>
81 <property name="maximum">
82 <number>100</number>
83 </property>
84 <property name="pageStep">
85 <number>10</number>
86 </property>
87 <property name="orientation">
88 <enum>Qt::Horizontal</enum>
89 </property>
90 </widget>
91 </item>
92 <item>
93 <widget class="QLabel" name="volume_indicator">
94 <property name="minimumSize">
95 <size>
96 <width>32</width>
97 <height>0</height>
98 </size>
99 </property>
100 <property name="text">
101 <string>0 %</string>
102 </property>
103 <property name="alignment">
104 <set>Qt::AlignCenter</set>
105 </property>
106 </widget>
107 </item>
108 </layout>
109 </item>
110 </layout>
111 </widget>
112 </item>
113 <item>
114 <spacer>
115 <property name="orientation">
116 <enum>Qt::Vertical</enum>
117 </property>
118 <property name="sizeHint" stdset="0">
119 <size>
120 <width>167</width>
121 <height>55</height>
122 </size>
123 </property>
124 </spacer>
125 </item>
126 </layout>
127 </widget>
128 <resources/>
129 <connections/>
130</ui>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 5e66239ff..45d84f19a 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -24,7 +24,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
24 }); 24 });
25} 25}
26 26
27ConfigureDebug::~ConfigureDebug() {} 27ConfigureDebug::~ConfigureDebug() = default;
28 28
29void ConfigureDebug::setConfiguration() { 29void ConfigureDebug::setConfiguration() {
30 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); 30 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
@@ -44,5 +44,4 @@ void ConfigureDebug::applyConfiguration() {
44 Log::Filter filter; 44 Log::Filter filter;
45 filter.ParseFilterString(Settings::values.log_filter); 45 filter.ParseFilterString(Settings::values.log_filter);
46 Log::SetGlobalFilter(filter); 46 Log::SetGlobalFilter(filter);
47 Settings::Apply();
48} 47}
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 118e91cf1..5ae7276bd 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -23,13 +23,6 @@
23 </property> 23 </property>
24 <layout class="QVBoxLayout" name="verticalLayout_3"> 24 <layout class="QVBoxLayout" name="verticalLayout_3">
25 <item> 25 <item>
26 <widget class="QLabel" name="label_1">
27 <property name="text">
28 <string>The GDB Stub only works correctly when the CPU JIT is off.</string>
29 </property>
30 </widget>
31 </item>
32 <item>
33 <layout class="QHBoxLayout" name="horizontalLayout_1"> 26 <layout class="QHBoxLayout" name="horizontalLayout_1">
34 <item> 27 <item>
35 <widget class="QCheckBox" name="toggle_gdbstub"> 28 <widget class="QCheckBox" name="toggle_gdbstub">
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 358f33005..cc4b326ae 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -6,13 +6,16 @@
6#include "ui_configure.h" 6#include "ui_configure.h"
7#include "yuzu/configuration/config.h" 7#include "yuzu/configuration/config.h"
8#include "yuzu/configuration/configure_dialog.h" 8#include "yuzu/configuration/configure_dialog.h"
9#include "yuzu/hotkeys.h"
9 10
10ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) { 11ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry)
12 : QDialog(parent), ui(new Ui::ConfigureDialog) {
11 ui->setupUi(this); 13 ui->setupUi(this);
14 ui->generalTab->PopulateHotkeyList(registry);
12 this->setConfiguration(); 15 this->setConfiguration();
13} 16}
14 17
15ConfigureDialog::~ConfigureDialog() {} 18ConfigureDialog::~ConfigureDialog() = default;
16 19
17void ConfigureDialog::setConfiguration() {} 20void ConfigureDialog::setConfiguration() {}
18 21
@@ -21,6 +24,7 @@ void ConfigureDialog::applyConfiguration() {
21 ui->systemTab->applyConfiguration(); 24 ui->systemTab->applyConfiguration();
22 ui->inputTab->applyConfiguration(); 25 ui->inputTab->applyConfiguration();
23 ui->graphicsTab->applyConfiguration(); 26 ui->graphicsTab->applyConfiguration();
27 ui->audioTab->applyConfiguration();
24 ui->debugTab->applyConfiguration(); 28 ui->debugTab->applyConfiguration();
25 Settings::Apply(); 29 Settings::Apply();
26} 30}
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 21fa1f501..bbbdacc29 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -7,6 +7,8 @@
7#include <memory> 7#include <memory>
8#include <QDialog> 8#include <QDialog>
9 9
10class HotkeyRegistry;
11
10namespace Ui { 12namespace Ui {
11class ConfigureDialog; 13class ConfigureDialog;
12} 14}
@@ -15,7 +17,7 @@ class ConfigureDialog : public QDialog {
15 Q_OBJECT 17 Q_OBJECT
16 18
17public: 19public:
18 explicit ConfigureDialog(QWidget* parent); 20 explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry);
19 ~ConfigureDialog(); 21 ~ConfigureDialog();
20 22
21 void applyConfiguration(); 23 void applyConfiguration();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index baa558667..d8caee1e8 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
24 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 24 ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
25} 25}
26 26
27ConfigureGeneral::~ConfigureGeneral() {} 27ConfigureGeneral::~ConfigureGeneral() = default;
28 28
29void ConfigureGeneral::setConfiguration() { 29void ConfigureGeneral::setConfiguration() {
30 ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); 30 ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
@@ -35,6 +35,10 @@ void ConfigureGeneral::setConfiguration() {
35 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); 35 ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
36} 36}
37 37
38void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
39 ui->widget->Populate(registry);
40}
41
38void ConfigureGeneral::applyConfiguration() { 42void ConfigureGeneral::applyConfiguration() {
39 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); 43 UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
40 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); 44 UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -44,5 +48,4 @@ void ConfigureGeneral::applyConfiguration() {
44 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked(); 48 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
45 Settings::values.use_multi_core = ui->use_multi_core->isChecked(); 49 Settings::values.use_multi_core = ui->use_multi_core->isChecked();
46 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked(); 50 Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
47 Settings::Apply();
48} 51}
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 447552d8c..4770034cc 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -7,6 +7,8 @@
7#include <memory> 7#include <memory>
8#include <QWidget> 8#include <QWidget>
9 9
10class HotkeyRegistry;
11
10namespace Ui { 12namespace Ui {
11class ConfigureGeneral; 13class ConfigureGeneral;
12} 14}
@@ -18,11 +20,11 @@ public:
18 explicit ConfigureGeneral(QWidget* parent = nullptr); 20 explicit ConfigureGeneral(QWidget* parent = nullptr);
19 ~ConfigureGeneral(); 21 ~ConfigureGeneral();
20 22
23 void PopulateHotkeyList(const HotkeyRegistry& registry);
21 void applyConfiguration(); 24 void applyConfiguration();
22 25
23private: 26private:
24 void setConfiguration(); 27 void setConfiguration();
25 28
26private:
27 std::unique_ptr<Ui::ConfigureGeneral> ui; 29 std::unique_ptr<Ui::ConfigureGeneral> ui;
28}; 30};
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 7664880d5..4afe0f81b 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -14,7 +14,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
14 this->setConfiguration(); 14 this->setConfiguration();
15} 15}
16 16
17ConfigureGraphics::~ConfigureGraphics() {} 17ConfigureGraphics::~ConfigureGraphics() = default;
18 18
19enum class Resolution : int { 19enum class Resolution : int {
20 Auto, 20 Auto,
@@ -67,5 +67,4 @@ void ConfigureGraphics::applyConfiguration() {
67 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); 67 ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
68 Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); 68 Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked();
69 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); 69 Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked();
70 Settings::Apply();
71} 70}
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 78559e2bb..5e7badedf 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -191,8 +191,6 @@ void ConfigureInput::applyConfiguration() {
191 [](const Common::ParamPackage& param) { return param.Serialize(); }); 191 [](const Common::ParamPackage& param) { return param.Serialize(); });
192 std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(), 192 std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(),
193 [](const Common::ParamPackage& param) { return param.Serialize(); }); 193 [](const Common::ParamPackage& param) { return param.Serialize(); });
194
195 Settings::Apply();
196} 194}
197 195
198void ConfigureInput::loadConfiguration() { 196void ConfigureInput::loadConfiguration() {
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index d09505a0f..e9ed9c38f 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -4,9 +4,10 @@
4 4
5#include <QMessageBox> 5#include <QMessageBox>
6#include "core/core.h" 6#include "core/core.h"
7#include "core/settings.h"
7#include "ui_configure_system.h" 8#include "ui_configure_system.h"
8#include "yuzu/configuration/configure_system.h" 9#include "yuzu/configuration/configure_system.h"
9#include "yuzu/ui_settings.h" 10#include "yuzu/main.h"
10 11
11static const std::array<int, 12> days_in_month = {{ 12static const std::array<int, 12> days_in_month = {{
12 31, 13 31,
@@ -34,10 +35,12 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
34 this->setConfiguration(); 35 this->setConfiguration();
35} 36}
36 37
37ConfigureSystem::~ConfigureSystem() {} 38ConfigureSystem::~ConfigureSystem() = default;
38 39
39void ConfigureSystem::setConfiguration() { 40void ConfigureSystem::setConfiguration() {
40 enabled = !Core::System::GetInstance().IsPoweredOn(); 41 enabled = !Core::System::GetInstance().IsPoweredOn();
42 ui->edit_username->setText(QString::fromStdString(Settings::values.username));
43 ui->combo_language->setCurrentIndex(Settings::values.language_index);
41} 44}
42 45
43void ConfigureSystem::ReadSystemSettings() {} 46void ConfigureSystem::ReadSystemSettings() {}
@@ -45,6 +48,9 @@ void ConfigureSystem::ReadSystemSettings() {}
45void ConfigureSystem::applyConfiguration() { 48void ConfigureSystem::applyConfiguration() {
46 if (!enabled) 49 if (!enabled)
47 return; 50 return;
51 Settings::values.username = ui->edit_username->text().toStdString();
52 Settings::values.language_index = ui->combo_language->currentIndex();
53 Settings::Apply();
48} 54}
49 55
50void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { 56void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) {
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui
index 8caf49623..f3f8db038 100644
--- a/src/yuzu/configuration/configure_system.ui
+++ b/src/yuzu/configuration/configure_system.ui
@@ -38,7 +38,7 @@
38 </sizepolicy> 38 </sizepolicy>
39 </property> 39 </property>
40 <property name="maxLength"> 40 <property name="maxLength">
41 <number>10</number> 41 <number>32</number>
42 </property> 42 </property>
43 </widget> 43 </widget>
44 </item> 44 </item>
@@ -164,7 +164,7 @@
164 </item> 164 </item>
165 <item> 165 <item>
166 <property name="text"> 166 <property name="text">
167 <string>Simplified Chinese (简体中文)</string> 167 <string>Chinese</string>
168 </property> 168 </property>
169 </item> 169 </item>
170 <item> 170 <item>
@@ -187,6 +187,31 @@
187 <string>Russian (Русский)</string> 187 <string>Russian (Русский)</string>
188 </property> 188 </property>
189 </item> 189 </item>
190 <item>
191 <property name="text">
192 <string>Taiwanese</string>
193 </property>
194 </item>
195 <item>
196 <property name="text">
197 <string>British English</string>
198 </property>
199 </item>
200 <item>
201 <property name="text">
202 <string>Canadian French</string>
203 </property>
204 </item>
205 <item>
206 <property name="text">
207 <string>Latin American Spanish</string>
208 </property>
209 </item>
210 <item>
211 <property name="text">
212 <string>Simplified Chinese</string>
213 </property>
214 </item>
190 <item> 215 <item>
191 <property name="text"> 216 <property name="text">
192 <string>Traditional Chinese (正體中文)</string> 217 <string>Traditional Chinese (正體中文)</string>
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
index d6d61a739..5f459ccfb 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
+++ b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.cpp
@@ -10,12 +10,12 @@ BreakPointObserverDock::BreakPointObserverDock(std::shared_ptr<Tegra::DebugConte
10 : QDockWidget(title, parent), BreakPointObserver(debug_context) { 10 : QDockWidget(title, parent), BreakPointObserver(debug_context) {
11 qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event"); 11 qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event");
12 12
13 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); 13 connect(this, &BreakPointObserverDock::Resumed, this, &BreakPointObserverDock::OnResumed);
14 14
15 // NOTE: This signal is emitted from a non-GUI thread, but connect() takes 15 // NOTE: This signal is emitted from a non-GUI thread, but connect() takes
16 // care of delaying its handling to the GUI thread. 16 // care of delaying its handling to the GUI thread.
17 connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), this, 17 connect(this, &BreakPointObserverDock::BreakPointHit, this,
18 SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection); 18 &BreakPointObserverDock::OnBreakPointHit, Qt::BlockingQueuedConnection);
19} 19}
20 20
21void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) { 21void BreakPointObserverDock::OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) {
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
index 9d05493cf..ab32f0115 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
+++ b/src/yuzu/debugger/graphics/graphics_breakpoint_observer.h
@@ -23,11 +23,11 @@ public:
23 void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override; 23 void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
24 void OnMaxwellResume() override; 24 void OnMaxwellResume() override;
25 25
26private slots:
27 virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0;
28 virtual void OnResumed() = 0;
29
30signals: 26signals:
31 void Resumed(); 27 void Resumed();
32 void BreakPointHit(Tegra::DebugContext::Event event, void* data); 28 void BreakPointHit(Tegra::DebugContext::Event event, void* data);
29
30private:
31 virtual void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) = 0;
32 virtual void OnResumed() = 0;
33}; 33};
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
index f98cc8152..eb16a38a0 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp
@@ -144,21 +144,25 @@ GraphicsBreakPointsWidget::GraphicsBreakPointsWidget(
144 144
145 qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event"); 145 qRegisterMetaType<Tegra::DebugContext::Event>("Tegra::DebugContext::Event");
146 146
147 connect(breakpoint_list, SIGNAL(doubleClicked(const QModelIndex&)), this, 147 connect(breakpoint_list, &QTreeView::doubleClicked, this,
148 SLOT(OnItemDoubleClicked(const QModelIndex&))); 148 &GraphicsBreakPointsWidget::OnItemDoubleClicked);
149 149
150 connect(resume_button, SIGNAL(clicked()), this, SLOT(OnResumeRequested())); 150 connect(resume_button, &QPushButton::clicked, this,
151 &GraphicsBreakPointsWidget::OnResumeRequested);
151 152
152 connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), this, 153 connect(this, &GraphicsBreakPointsWidget::BreakPointHit, this,
153 SLOT(OnBreakPointHit(Tegra::DebugContext::Event, void*)), Qt::BlockingQueuedConnection); 154 &GraphicsBreakPointsWidget::OnBreakPointHit, Qt::BlockingQueuedConnection);
154 connect(this, SIGNAL(Resumed()), this, SLOT(OnResumed())); 155 connect(this, &GraphicsBreakPointsWidget::Resumed, this, &GraphicsBreakPointsWidget::OnResumed);
155 156
156 connect(this, SIGNAL(BreakPointHit(Tegra::DebugContext::Event, void*)), breakpoint_model, 157 connect(this, &GraphicsBreakPointsWidget::BreakPointHit, breakpoint_model,
157 SLOT(OnBreakPointHit(Tegra::DebugContext::Event)), Qt::BlockingQueuedConnection); 158 &BreakPointModel::OnBreakPointHit, Qt::BlockingQueuedConnection);
158 connect(this, SIGNAL(Resumed()), breakpoint_model, SLOT(OnResumed())); 159 connect(this, &GraphicsBreakPointsWidget::Resumed, breakpoint_model,
160 &BreakPointModel::OnResumed);
159 161
160 connect(this, SIGNAL(BreakPointsChanged(const QModelIndex&, const QModelIndex&)), 162 connect(this, &GraphicsBreakPointsWidget::BreakPointsChanged,
161 breakpoint_model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&))); 163 [this](const QModelIndex& top_left, const QModelIndex& bottom_right) {
164 breakpoint_model->dataChanged(top_left, bottom_right);
165 });
162 166
163 QWidget* main_widget = new QWidget; 167 QWidget* main_widget = new QWidget;
164 auto main_layout = new QVBoxLayout; 168 auto main_layout = new QVBoxLayout;
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.h b/src/yuzu/debugger/graphics/graphics_breakpoints.h
index ae0ede2e8..a920a2ae5 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints.h
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints.h
@@ -26,18 +26,17 @@ public:
26 void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override; 26 void OnMaxwellBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
27 void OnMaxwellResume() override; 27 void OnMaxwellResume() override;
28 28
29public slots:
30 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data);
31 void OnItemDoubleClicked(const QModelIndex&);
32 void OnResumeRequested();
33 void OnResumed();
34
35signals: 29signals:
36 void Resumed(); 30 void Resumed();
37 void BreakPointHit(Tegra::DebugContext::Event event, void* data); 31 void BreakPointHit(Tegra::DebugContext::Event event, void* data);
38 void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); 32 void BreakPointsChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
39 33
40private: 34private:
35 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data);
36 void OnItemDoubleClicked(const QModelIndex&);
37 void OnResumeRequested();
38 void OnResumed();
39
41 QLabel* status_text; 40 QLabel* status_text;
42 QPushButton* resume_button; 41 QPushButton* resume_button;
43 42
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
index 35a6876ae..7112b87e6 100644
--- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
+++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h
@@ -25,7 +25,6 @@ public:
25 25
26 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; 26 bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
27 27
28public slots:
29 void OnBreakPointHit(Tegra::DebugContext::Event event); 28 void OnBreakPointHit(Tegra::DebugContext::Event event);
30 void OnResumed(); 29 void OnResumed();
31 30
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index c41ff693b..3f7103ab9 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -34,7 +34,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
34 34
35SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) 35SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
36 : QLabel(parent), surface_widget(surface_widget_) {} 36 : QLabel(parent), surface_widget(surface_widget_) {}
37SurfacePicture::~SurfacePicture() {} 37
38SurfacePicture::~SurfacePicture() = default;
38 39
39void SurfacePicture::mousePressEvent(QMouseEvent* event) { 40void SurfacePicture::mousePressEvent(QMouseEvent* event) {
40 // Only do something while the left mouse button is held down 41 // Only do something while the left mouse button is held down
@@ -153,22 +154,24 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext
153 save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save")); 154 save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
154 155
155 // Connections 156 // Connections
156 connect(this, SIGNAL(Update()), this, SLOT(OnUpdate())); 157 connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate);
157 connect(surface_source_list, SIGNAL(currentIndexChanged(int)), this, 158 connect(surface_source_list,
158 SLOT(OnSurfaceSourceChanged(int))); 159 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
159 connect(surface_address_control, SIGNAL(ValueChanged(qint64)), this, 160 &GraphicsSurfaceWidget::OnSurfaceSourceChanged);
160 SLOT(OnSurfaceAddressChanged(qint64))); 161 connect(surface_address_control, &CSpinBox::ValueChanged, this,
161 connect(surface_width_control, SIGNAL(valueChanged(int)), this, 162 &GraphicsSurfaceWidget::OnSurfaceAddressChanged);
162 SLOT(OnSurfaceWidthChanged(int))); 163 connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
163 connect(surface_height_control, SIGNAL(valueChanged(int)), this, 164 this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged);
164 SLOT(OnSurfaceHeightChanged(int))); 165 connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
165 connect(surface_format_control, SIGNAL(currentIndexChanged(int)), this, 166 this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged);
166 SLOT(OnSurfaceFormatChanged(int))); 167 connect(surface_format_control,
167 connect(surface_picker_x_control, SIGNAL(valueChanged(int)), this, 168 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
168 SLOT(OnSurfacePickerXChanged(int))); 169 &GraphicsSurfaceWidget::OnSurfaceFormatChanged);
169 connect(surface_picker_y_control, SIGNAL(valueChanged(int)), this, 170 connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
170 SLOT(OnSurfacePickerYChanged(int))); 171 this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged);
171 connect(save_surface, SIGNAL(clicked()), this, SLOT(SaveSurface())); 172 connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
173 this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged);
174 connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface);
172 175
173 auto main_widget = new QWidget; 176 auto main_widget = new QWidget;
174 auto main_layout = new QVBoxLayout; 177 auto main_layout = new QVBoxLayout;
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
index 6a344bdfc..323e39d94 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ b/src/yuzu/debugger/graphics/graphics_surface.h
@@ -22,11 +22,11 @@ class SurfacePicture : public QLabel {
22public: 22public:
23 explicit SurfacePicture(QWidget* parent = nullptr, 23 explicit SurfacePicture(QWidget* parent = nullptr,
24 GraphicsSurfaceWidget* surface_widget = nullptr); 24 GraphicsSurfaceWidget* surface_widget = nullptr);
25 ~SurfacePicture(); 25 ~SurfacePicture() override;
26 26
27protected slots: 27protected slots:
28 virtual void mouseMoveEvent(QMouseEvent* event); 28 void mouseMoveEvent(QMouseEvent* event) override;
29 virtual void mousePressEvent(QMouseEvent* event); 29 void mousePressEvent(QMouseEvent* event) override;
30 30
31private: 31private:
32 GraphicsSurfaceWidget* surface_widget; 32 GraphicsSurfaceWidget* surface_widget;
@@ -65,16 +65,15 @@ public slots:
65 void OnSurfacePickerYChanged(int new_value); 65 void OnSurfacePickerYChanged(int new_value);
66 void OnUpdate(); 66 void OnUpdate();
67 67
68private slots: 68signals:
69 void Update();
70
71private:
69 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override; 72 void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
70 void OnResumed() override; 73 void OnResumed() override;
71 74
72 void SaveSurface(); 75 void SaveSurface();
73 76
74signals:
75 void Update();
76
77private:
78 QComboBox* surface_source_list; 77 QComboBox* surface_source_list;
79 CSpinBox* surface_address_control; 78 CSpinBox* surface_address_control;
80 QSpinBox* surface_width_control; 79 QSpinBox* surface_width_control;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f5a5697a0..d0926d723 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -14,7 +14,7 @@
14#include "core/hle/kernel/timer.h" 14#include "core/hle/kernel/timer.h"
15#include "core/hle/kernel/wait_object.h" 15#include "core/hle/kernel/wait_object.h"
16 16
17WaitTreeItem::~WaitTreeItem() {} 17WaitTreeItem::~WaitTreeItem() = default;
18 18
19QColor WaitTreeItem::GetColor() const { 19QColor WaitTreeItem::GetColor() const {
20 return QColor(Qt::GlobalColor::black); 20 return QColor(Qt::GlobalColor::black);
@@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
316 316
317 list.push_back(std::make_unique<WaitTreeText>( 317 list.push_back(std::make_unique<WaitTreeText>(
318 tr("reset type = %1") 318 tr("reset type = %1")
319 .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type)))); 319 .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
320 return list; 320 return list;
321} 321}
322 322
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 10fc9e968..513b3c45d 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -9,7 +9,7 @@
9#include <QTreeView> 9#include <QTreeView>
10#include <boost/container/flat_set.hpp> 10#include <boost/container/flat_set.hpp>
11#include "core/core.h" 11#include "core/core.h"
12#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/object.h"
13 13
14class EmuThread; 14class EmuThread;
15 15
@@ -25,11 +25,13 @@ class WaitTreeThread;
25class WaitTreeItem : public QObject { 25class WaitTreeItem : public QObject {
26 Q_OBJECT 26 Q_OBJECT
27public: 27public:
28 ~WaitTreeItem() override;
29
28 virtual bool IsExpandable() const; 30 virtual bool IsExpandable() const;
29 virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const; 31 virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
30 virtual QString GetText() const = 0; 32 virtual QString GetText() const = 0;
31 virtual QColor GetColor() const; 33 virtual QColor GetColor() const;
32 virtual ~WaitTreeItem(); 34
33 void Expand(); 35 void Expand();
34 WaitTreeItem* Parent() const; 36 WaitTreeItem* Parent() const;
35 const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const; 37 const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 99e6634a1..24f38a3c7 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -162,15 +162,15 @@ void GameList::onTextChanged(const QString& newText) {
162 } 162 }
163 search_field->setFilterResult(rowCount, rowCount); 163 search_field->setFilterResult(rowCount, rowCount);
164 } else { 164 } else {
165 QStandardItem* child_file;
166 QString file_path, file_name, file_title, file_programmid;
167 int result_count = 0; 165 int result_count = 0;
168 for (int i = 0; i < rowCount; ++i) { 166 for (int i = 0; i < rowCount; ++i) {
169 child_file = item_model->item(i, 0); 167 const QStandardItem* child_file = item_model->item(i, 0);
170 file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower(); 168 const QString file_path =
171 file_name = file_path.mid(file_path.lastIndexOf("/") + 1); 169 child_file->data(GameListItemPath::FullPathRole).toString().toLower();
172 file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower(); 170 QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
173 file_programmid = 171 const QString file_title =
172 child_file->data(GameListItemPath::TitleRole).toString().toLower();
173 const QString file_programmid =
174 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower(); 174 child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
175 175
176 // Only items which filename in combination with its title contains all words 176 // Only items which filename in combination with its title contains all words
@@ -258,18 +258,20 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
258 258
259void GameList::ValidateEntry(const QModelIndex& item) { 259void GameList::ValidateEntry(const QModelIndex& item) {
260 // We don't care about the individual QStandardItem that was selected, but its row. 260 // We don't care about the individual QStandardItem that was selected, but its row.
261 int row = item_model->itemFromIndex(item)->row(); 261 const int row = item_model->itemFromIndex(item)->row();
262 QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); 262 const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
263 QString file_path = child_file->data(GameListItemPath::FullPathRole).toString(); 263 const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
264 264
265 if (file_path.isEmpty()) 265 if (file_path.isEmpty())
266 return; 266 return;
267 std::string std_file_path(file_path.toStdString()); 267
268 if (!FileUtil::Exists(std_file_path)) 268 if (!QFileInfo::exists(file_path))
269 return; 269 return;
270 if (FileUtil::IsDirectory(std_file_path)) { 270
271 QDir dir(std_file_path.c_str()); 271 const QFileInfo file_info{file_path};
272 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); 272 if (file_info.isDir()) {
273 const QDir dir{file_path};
274 const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
273 if (matching_main.size() == 1) { 275 if (matching_main.size() == 1) {
274 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]); 276 emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
275 } 277 }
@@ -365,24 +367,26 @@ void GameList::LoadInterfaceLayout() {
365 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); 367 item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
366} 368}
367 369
368const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca"}; 370const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
369 371
370static bool HasSupportedFileExtension(const std::string& file_name) { 372static bool HasSupportedFileExtension(const std::string& file_name) {
371 QFileInfo file = QFileInfo(file_name.c_str()); 373 const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
372 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); 374 return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
373} 375}
374 376
375static bool IsExtractedNCAMain(const std::string& file_name) { 377static bool IsExtractedNCAMain(const std::string& file_name) {
376 return QFileInfo(file_name.c_str()).fileName() == "main"; 378 return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
377} 379}
378 380
379static QString FormatGameName(const std::string& physical_name) { 381static QString FormatGameName(const std::string& physical_name) {
380 QFileInfo file_info(physical_name.c_str()); 382 const QString physical_name_as_qstring = QString::fromStdString(physical_name);
383 const QFileInfo file_info(physical_name_as_qstring);
384
381 if (IsExtractedNCAMain(physical_name)) { 385 if (IsExtractedNCAMain(physical_name)) {
382 return file_info.dir().path(); 386 return file_info.dir().path();
383 } else {
384 return QString::fromStdString(physical_name);
385 } 387 }
388
389 return physical_name_as_qstring;
386} 390}
387 391
388void GameList::RefreshGameDirectory() { 392void GameList::RefreshGameDirectory() {
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index a758b77aa..aa69a098f 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <atomic>
8#include <utility>
8#include <QImage> 9#include <QImage>
9#include <QRunnable> 10#include <QRunnable>
10#include <QStandardItem> 11#include <QStandardItem>
@@ -27,9 +28,8 @@ static QPixmap GetDefaultIcon(bool large) {
27class GameListItem : public QStandardItem { 28class GameListItem : public QStandardItem {
28 29
29public: 30public:
30 GameListItem() : QStandardItem() {} 31 GameListItem() = default;
31 GameListItem(const QString& string) : QStandardItem(string) {} 32 explicit GameListItem(const QString& string) : QStandardItem(string) {}
32 virtual ~GameListItem() override {}
33}; 33};
34 34
35/** 35/**
@@ -45,9 +45,8 @@ public:
45 static const int TitleRole = Qt::UserRole + 2; 45 static const int TitleRole = Qt::UserRole + 2;
46 static const int ProgramIdRole = Qt::UserRole + 3; 46 static const int ProgramIdRole = Qt::UserRole + 3;
47 47
48 GameListItemPath() : GameListItem() {} 48 GameListItemPath() = default;
49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) 49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
50 : GameListItem() {
51 setData(game_path, FullPathRole); 50 setData(game_path, FullPathRole);
52 setData(qulonglong(program_id), ProgramIdRole); 51 setData(qulonglong(program_id), ProgramIdRole);
53 } 52 }
@@ -75,8 +74,8 @@ class GameListItemSize : public GameListItem {
75public: 74public:
76 static const int SizeRole = Qt::UserRole + 1; 75 static const int SizeRole = Qt::UserRole + 1;
77 76
78 GameListItemSize() : GameListItem() {} 77 GameListItemSize() = default;
79 GameListItemSize(const qulonglong size_bytes) : GameListItem() { 78 explicit GameListItemSize(const qulonglong size_bytes) {
80 setData(size_bytes, SizeRole); 79 setData(size_bytes, SizeRole);
81 } 80 }
82 81
@@ -111,7 +110,7 @@ class GameListWorker : public QObject, public QRunnable {
111 110
112public: 111public:
113 GameListWorker(QString dir_path, bool deep_scan) 112 GameListWorker(QString dir_path, bool deep_scan)
114 : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {} 113 : dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
115 114
116public slots: 115public slots:
117 /// Starts the processing of directory tree information. 116 /// Starts the processing of directory tree information.
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 61acb38ee..dce399774 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -10,58 +10,53 @@
10#include "yuzu/hotkeys.h" 10#include "yuzu/hotkeys.h"
11#include "yuzu/ui_settings.h" 11#include "yuzu/ui_settings.h"
12 12
13struct Hotkey { 13HotkeyRegistry::HotkeyRegistry() = default;
14 Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} 14HotkeyRegistry::~HotkeyRegistry() = default;
15 15
16 QKeySequence keyseq; 16void HotkeyRegistry::LoadHotkeys() {
17 QShortcut* shortcut;
18 Qt::ShortcutContext context;
19};
20
21typedef std::map<QString, Hotkey> HotkeyMap;
22typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
23
24HotkeyGroupMap hotkey_groups;
25
26void SaveHotkeys() {
27 UISettings::values.shortcuts.clear();
28 for (auto group : hotkey_groups) {
29 for (auto hotkey : group.second) {
30 UISettings::values.shortcuts.emplace_back(
31 UISettings::Shortcut(group.first + "/" + hotkey.first,
32 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
33 hotkey.second.context)));
34 }
35 }
36}
37
38void LoadHotkeys() {
39 // Make sure NOT to use a reference here because it would become invalid once we call 17 // Make sure NOT to use a reference here because it would become invalid once we call
40 // beginGroup() 18 // beginGroup()
41 for (auto shortcut : UISettings::values.shortcuts) { 19 for (auto shortcut : UISettings::values.shortcuts) {
42 QStringList cat = shortcut.first.split("/"); 20 const QStringList cat = shortcut.first.split('/');
43 Q_ASSERT(cat.size() >= 2); 21 Q_ASSERT(cat.size() >= 2);
44 22
45 // RegisterHotkey assigns default keybindings, so use old values as default parameters 23 // RegisterHotkey assigns default keybindings, so use old values as default parameters
46 Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; 24 Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
47 if (!shortcut.second.first.isEmpty()) { 25 if (!shortcut.second.first.isEmpty()) {
48 hk.keyseq = QKeySequence::fromString(shortcut.second.first); 26 hk.keyseq = QKeySequence::fromString(shortcut.second.first);
49 hk.context = (Qt::ShortcutContext)shortcut.second.second; 27 hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
50 } 28 }
51 if (hk.shortcut) 29 if (hk.shortcut)
52 hk.shortcut->setKey(hk.keyseq); 30 hk.shortcut->setKey(hk.keyseq);
53 } 31 }
54} 32}
55 33
56void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq, 34void HotkeyRegistry::SaveHotkeys() {
57 Qt::ShortcutContext default_context) { 35 UISettings::values.shortcuts.clear();
58 if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) { 36 for (const auto& group : hotkey_groups) {
59 hotkey_groups[group][action].keyseq = default_keyseq; 37 for (const auto& hotkey : group.second) {
60 hotkey_groups[group][action].context = default_context; 38 UISettings::values.shortcuts.emplace_back(
39 UISettings::Shortcut(group.first + '/' + hotkey.first,
40 UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
41 hotkey.second.context)));
42 }
61 } 43 }
62} 44}
63 45
64QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) { 46void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action,
47 const QKeySequence& default_keyseq,
48 Qt::ShortcutContext default_context) {
49 auto& hotkey_group = hotkey_groups[group];
50 if (hotkey_group.find(action) != hotkey_group.end()) {
51 return;
52 }
53
54 auto& hotkey_action = hotkey_groups[group][action];
55 hotkey_action.keyseq = default_keyseq;
56 hotkey_action.context = default_context;
57}
58
59QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
65 Hotkey& hk = hotkey_groups[group][action]; 60 Hotkey& hk = hotkey_groups[group][action];
66 61
67 if (!hk.shortcut) 62 if (!hk.shortcut)
@@ -72,10 +67,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
72 67
73GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { 68GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
74 ui.setupUi(this); 69 ui.setupUi(this);
70}
75 71
76 for (auto group : hotkey_groups) { 72void GHotkeysDialog::Populate(const HotkeyRegistry& registry) {
73 for (const auto& group : registry.hotkey_groups) {
77 QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); 74 QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
78 for (auto hotkey : group.second) { 75 for (const auto& hotkey : group.second) {
79 QStringList columns; 76 QStringList columns;
80 columns << hotkey.first << hotkey.second.keyseq.toString(); 77 columns << hotkey.first << hotkey.second.keyseq.toString();
81 QTreeWidgetItem* item = new QTreeWidgetItem(columns); 78 QTreeWidgetItem* item = new QTreeWidgetItem(columns);
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index a4ccc193b..f38e6c002 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include "ui_hotkeys.h" 8#include "ui_hotkeys.h"
8 9
9class QDialog; 10class QDialog;
@@ -11,47 +12,69 @@ class QKeySequence;
11class QSettings; 12class QSettings;
12class QShortcut; 13class QShortcut;
13 14
14/** 15class HotkeyRegistry final {
15 * Register a hotkey. 16public:
16 * 17 friend class GHotkeysDialog;
17 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") 18
18 * @param action Name of the action (e.g. "Start Emulation", "Load Image") 19 explicit HotkeyRegistry();
19 * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings 20 ~HotkeyRegistry();
20 * file before 21
21 * @param default_context Default context to assign if the hotkey wasn't present in the settings 22 /**
22 * file before 23 * Loads hotkeys from the settings file.
23 * @warning Both the group and action strings will be displayed in the hotkey settings dialog 24 *
24 */ 25 * @note Yet unregistered hotkeys which are present in the settings will automatically be
25void RegisterHotkey(const QString& group, const QString& action, 26 * registered.
26 const QKeySequence& default_keyseq = QKeySequence(), 27 */
27 Qt::ShortcutContext default_context = Qt::WindowShortcut); 28 void LoadHotkeys();
28 29
29/** 30 /**
30 * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots. 31 * Saves all registered hotkeys to the settings file.
31 * 32 *
32 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). 33 * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
33 * @param action Name of the action (e.g. "Start Emulation", "Load Image"). 34 * settings group will be created to store the key sequence and the hotkey context.
34 * @param widget Parent widget of the returned QShortcut. 35 */
35 * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut 36 void SaveHotkeys();
36 * will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent. 37
37 */ 38 /**
38QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); 39 * Returns a QShortcut object whose activated() signal can be connected to other QObjects'
39 40 * slots.
40/** 41 *
41 * Saves all registered hotkeys to the settings file. 42 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
42 * 43 * @param action Name of the action (e.g. "Start Emulation", "Load Image").
43 * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a 44 * @param widget Parent widget of the returned QShortcut.
44 * settings group will be created to store the key sequence and the hotkey context. 45 * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
45 */ 46 * will be the same. Thus, you shouldn't rely on the caller really being the
46void SaveHotkeys(); 47 * QShortcut's parent.
47 48 */
48/** 49 QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
49 * Loads hotkeys from the settings file. 50
50 * 51 /**
51 * @note Yet unregistered hotkeys which are present in the settings will automatically be 52 * Register a hotkey.
52 * registered. 53 *
53 */ 54 * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
54void LoadHotkeys(); 55 * @param action Name of the action (e.g. "Start Emulation", "Load Image")
56 * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the
57 * settings file before
58 * @param default_context Default context to assign if the hotkey wasn't present in the settings
59 * file before
60 * @warning Both the group and action strings will be displayed in the hotkey settings dialog
61 */
62 void RegisterHotkey(const QString& group, const QString& action,
63 const QKeySequence& default_keyseq = {},
64 Qt::ShortcutContext default_context = Qt::WindowShortcut);
65
66private:
67 struct Hotkey {
68 QKeySequence keyseq;
69 QShortcut* shortcut = nullptr;
70 Qt::ShortcutContext context = Qt::WindowShortcut;
71 };
72
73 using HotkeyMap = std::map<QString, Hotkey>;
74 using HotkeyGroupMap = std::map<QString, HotkeyMap>;
75
76 HotkeyGroupMap hotkey_groups;
77};
55 78
56class GHotkeysDialog : public QWidget { 79class GHotkeysDialog : public QWidget {
57 Q_OBJECT 80 Q_OBJECT
@@ -59,6 +82,8 @@ class GHotkeysDialog : public QWidget {
59public: 82public:
60 explicit GHotkeysDialog(QWidget* parent = nullptr); 83 explicit GHotkeysDialog(QWidget* parent = nullptr);
61 84
85 void Populate(const HotkeyRegistry& registry);
86
62private: 87private:
63 Ui::hotkeys ui; 88 Ui::hotkeys ui;
64}; 89};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 97273f967..17ed62c72 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -23,6 +23,7 @@
23#include "common/scope_exit.h" 23#include "common/scope_exit.h"
24#include "common/string_util.h" 24#include "common/string_util.h"
25#include "core/core.h" 25#include "core/core.h"
26#include "core/crypto/key_manager.h"
26#include "core/gdbstub/gdbstub.h" 27#include "core/gdbstub/gdbstub.h"
27#include "core/loader/loader.h" 28#include "core/loader/loader.h"
28#include "core/settings.h" 29#include "core/settings.h"
@@ -80,6 +81,8 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
80 81
81void GMainWindow::ShowCallouts() {} 82void GMainWindow::ShowCallouts() {}
82 83
84const int GMainWindow::max_recent_files_item;
85
83GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { 86GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
84 87
85 debug_context = Tegra::DebugContext::Construct(); 88 debug_context = Tegra::DebugContext::Construct();
@@ -205,27 +208,46 @@ void GMainWindow::InitializeRecentFileMenuActions() {
205} 208}
206 209
207void GMainWindow::InitializeHotkeys() { 210void GMainWindow::InitializeHotkeys() {
208 RegisterHotkey("Main Window", "Load File", QKeySequence::Open); 211 hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
209 RegisterHotkey("Main Window", "Start Emulation"); 212 hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
210 RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); 213 hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
211 RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), 214 hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
212 Qt::ApplicationShortcut); 215 hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
213 LoadHotkeys(); 216 Qt::ApplicationShortcut);
214 217 hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
215 connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this, 218 Qt::ApplicationShortcut);
216 &GMainWindow::OnMenuLoadFile); 219 hotkey_registry.LoadHotkeys();
217 connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this, 220
218 &GMainWindow::OnStartGame); 221 connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
219 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated, 222 this, &GMainWindow::OnMenuLoadFile);
220 ui.action_Fullscreen, &QAction::trigger); 223 connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this),
221 connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously, 224 &QShortcut::activated, this, &GMainWindow::OnStartGame);
222 ui.action_Fullscreen, &QAction::trigger); 225 connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
223 connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] { 226 this, [&] {
224 if (emulation_running) { 227 if (emulation_running) {
225 ui.action_Fullscreen->setChecked(false); 228 if (emu_thread->IsRunning()) {
226 ToggleFullscreen(); 229 OnPauseGame();
227 } 230 } else {
228 }); 231 OnStartGame();
232 }
233 }
234 });
235 connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
236 &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger);
237 connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
238 &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger);
239 connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this),
240 &QShortcut::activated, this, [&] {
241 if (emulation_running) {
242 ui.action_Fullscreen->setChecked(false);
243 ToggleFullscreen();
244 }
245 });
246 connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this),
247 &QShortcut::activated, this, [&] {
248 Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
249 UpdateStatusBar();
250 });
229} 251}
230 252
231void GMainWindow::SetDefaultUIGeometry() { 253void GMainWindow::SetDefaultUIGeometry() {
@@ -304,7 +326,8 @@ void GMainWindow::ConnectMenuEvents() {
304 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); 326 connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
305 327
306 // Fullscreen 328 // Fullscreen
307 ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key()); 329 ui.action_Fullscreen->setShortcut(
330 hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
308 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen); 331 connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
309 332
310 // Help 333 // Help
@@ -386,7 +409,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
386 409
387 system.SetGPUDebugContext(debug_context); 410 system.SetGPUDebugContext(debug_context);
388 411
389 const Core::System::ResultStatus result{system.Load(render_window, filename.toStdString())}; 412 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
390 413
391 render_window->DoneCurrent(); 414 render_window->DoneCurrent();
392 415
@@ -408,18 +431,49 @@ bool GMainWindow::LoadROM(const QString& filename) {
408 tr("Could not determine the system mode.")); 431 tr("Could not determine the system mode."));
409 break; 432 break;
410 433
411 case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: { 434 case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys: {
435 const auto reg_found = Core::Crypto::KeyManager::KeyFileExists(false);
436 const auto title_found = Core::Crypto::KeyManager::KeyFileExists(true);
437
438 std::string file_text;
439
440 if (!reg_found && !title_found) {
441 file_text = "A proper key file (prod.keys, dev.keys, or title.keys) could not be "
442 "found. You will need to dump your keys from your switch to continue.";
443 } else if (reg_found && title_found) {
444 file_text =
445 "Both key files were found in your config directory, but the correct key could"
446 "not be found. You may be missing a titlekey or general key, depending on "
447 "the game.";
448 } else if (reg_found) {
449 file_text =
450 "The regular keys file (prod.keys/dev.keys) was found in your config, but the "
451 "titlekeys file (title.keys) was not. You are either missing the correct "
452 "titlekey or missing a general key required to decrypt the game.";
453 } else {
454 file_text = "The title keys file (title.keys) was found in your config, but "
455 "the regular keys file (prod.keys/dev.keys) was not. Unfortunately, "
456 "having the titlekey is not enough, you need additional general keys "
457 "to properly decrypt the game. You should double-check to make sure "
458 "your keys are correct.";
459 }
460
412 QMessageBox::critical( 461 QMessageBox::critical(
413 this, tr("Error while loading ROM!"), 462 this, tr("Error while loading ROM!"),
414 tr("The game that you are trying to load must be decrypted before being used with " 463 tr(("The game you are trying to load is encrypted and the required keys to load "
415 "yuzu. A real Switch is required.<br/><br/>" 464 "the game could not be found in your configuration. " +
416 "For more information on dumping and decrypting games, please see the following " 465 file_text + " Please refer to the yuzu wiki for help.")
417 "wiki pages: <ul>" 466 .c_str()));
418 "<li><a href='https://yuzu-emu.org/wiki/dumping-game-cartridges/'>Dumping Game " 467 break;
419 "Cartridges</a></li>" 468 }
420 "<li><a href='https://yuzu-emu.org/wiki/dumping-installed-titles/'>Dumping " 469 case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting: {
421 "Installed Titles</a></li>" 470 QMessageBox::critical(
422 "</ul>")); 471 this, tr("Error while loading ROM!"),
472 tr("There was a general error while decrypting the game. This means that the keys "
473 "necessary were found, but were either incorrect, the game itself was not a "
474 "valid game or the game uses an unhandled cryptographic scheme. Please double "
475 "check that you have the correct "
476 "keys."));
423 break; 477 break;
424 } 478 }
425 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: 479 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
@@ -429,7 +483,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
429 483
430 case Core::System::ResultStatus::ErrorVideoCore: 484 case Core::System::ResultStatus::ErrorVideoCore:
431 QMessageBox::critical( 485 QMessageBox::critical(
432 this, tr("An error occured in the video core."), 486 this, tr("An error occurred initializing the video core."),
433 tr("yuzu has encountered an error while running the video core, please see the " 487 tr("yuzu has encountered an error while running the video core, please see the "
434 "log for more details." 488 "log for more details."
435 "For more information on accessing the log, please see the following page: " 489 "For more information on accessing the log, please see the following page: "
@@ -443,7 +497,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
443 default: 497 default:
444 QMessageBox::critical( 498 QMessageBox::critical(
445 this, tr("Error while loading ROM!"), 499 this, tr("Error while loading ROM!"),
446 tr("An unknown error occured. Please see the log for more details.")); 500 tr("An unknown error occurred. Please see the log for more details."));
447 break; 501 break;
448 } 502 }
449 return false; 503 return false;
@@ -531,11 +585,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
531} 585}
532 586
533void GMainWindow::UpdateRecentFiles() { 587void GMainWindow::UpdateRecentFiles() {
534 unsigned int num_recent_files = 588 const int num_recent_files =
535 std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item)); 589 std::min(UISettings::values.recent_files.size(), max_recent_files_item);
536 590
537 for (unsigned int i = 0; i < num_recent_files; i++) { 591 for (int i = 0; i < num_recent_files; i++) {
538 QString text = QString("&%1. %2").arg(i + 1).arg( 592 const QString text = QString("&%1. %2").arg(i + 1).arg(
539 QFileInfo(UISettings::values.recent_files[i]).fileName()); 593 QFileInfo(UISettings::values.recent_files[i]).fileName());
540 actions_recent_files[i]->setText(text); 594 actions_recent_files[i]->setText(text);
541 actions_recent_files[i]->setData(UISettings::values.recent_files[i]); 595 actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
@@ -547,12 +601,8 @@ void GMainWindow::UpdateRecentFiles() {
547 actions_recent_files[j]->setVisible(false); 601 actions_recent_files[j]->setVisible(false);
548 } 602 }
549 603
550 // Grey out the recent files menu if the list is empty 604 // Enable the recent files menu if the list isn't empty
551 if (num_recent_files == 0) { 605 ui.menu_recent_files->setEnabled(num_recent_files != 0);
552 ui.menu_recent_files->setEnabled(false);
553 } else {
554 ui.menu_recent_files->setEnabled(true);
555 }
556} 606}
557 607
558void GMainWindow::OnGameListLoadFile(QString game_path) { 608void GMainWindow::OnGameListLoadFile(QString game_path) {
@@ -583,9 +633,15 @@ void GMainWindow::OnMenuLoadFile() {
583} 633}
584 634
585void GMainWindow::OnMenuLoadFolder() { 635void GMainWindow::OnMenuLoadFolder() {
586 QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory")); 636 const QString dir_path =
637 QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
638
639 if (dir_path.isNull()) {
640 return;
641 }
587 642
588 QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files); 643 const QDir dir{dir_path};
644 const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
589 if (matching_main.size() == 1) { 645 if (matching_main.size() == 1) {
590 BootGame(dir.path() + DIR_SEP + matching_main[0]); 646 BootGame(dir.path() + DIR_SEP + matching_main[0]);
591 } else { 647 } else {
@@ -606,9 +662,8 @@ void GMainWindow::OnMenuRecentFile() {
606 QAction* action = qobject_cast<QAction*>(sender()); 662 QAction* action = qobject_cast<QAction*>(sender());
607 assert(action); 663 assert(action);
608 664
609 QString filename = action->data().toString(); 665 const QString filename = action->data().toString();
610 QFileInfo file_info(filename); 666 if (QFileInfo::exists(filename)) {
611 if (file_info.exists()) {
612 BootGame(filename); 667 BootGame(filename);
613 } else { 668 } else {
614 // Display an error message and remove the file from the list. 669 // Display an error message and remove the file from the list.
@@ -706,11 +761,13 @@ void GMainWindow::ToggleWindowMode() {
706} 761}
707 762
708void GMainWindow::OnConfigure() { 763void GMainWindow::OnConfigure() {
709 ConfigureDialog configureDialog(this); 764 ConfigureDialog configureDialog(this, hotkey_registry);
765 auto old_theme = UISettings::values.theme;
710 auto result = configureDialog.exec(); 766 auto result = configureDialog.exec();
711 if (result == QDialog::Accepted) { 767 if (result == QDialog::Accepted) {
712 configureDialog.applyConfiguration(); 768 configureDialog.applyConfiguration();
713 UpdateUITheme(); 769 if (UISettings::values.theme != old_theme)
770 UpdateUITheme();
714 config->Save(); 771 config->Save();
715 } 772 }
716} 773}
@@ -843,7 +900,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
843 UISettings::values.first_start = false; 900 UISettings::values.first_start = false;
844 901
845 game_list->SaveInterfaceLayout(); 902 game_list->SaveInterfaceLayout();
846 SaveHotkeys(); 903 hotkey_registry.SaveHotkeys();
847 904
848 // Shutdown session if the emu thread is active... 905 // Shutdown session if the emu thread is active...
849 if (emu_thread != nullptr) 906 if (emu_thread != nullptr)
@@ -897,15 +954,14 @@ void GMainWindow::UpdateUITheme() {
897 QStringList theme_paths(default_theme_paths); 954 QStringList theme_paths(default_theme_paths);
898 if (UISettings::values.theme != UISettings::themes[0].second && 955 if (UISettings::values.theme != UISettings::themes[0].second &&
899 !UISettings::values.theme.isEmpty()) { 956 !UISettings::values.theme.isEmpty()) {
900 QString theme_uri(":" + UISettings::values.theme + "/style.qss"); 957 const QString theme_uri(":" + UISettings::values.theme + "/style.qss");
901 QFile f(theme_uri); 958 QFile f(theme_uri);
902 if (!f.exists()) { 959 if (f.open(QFile::ReadOnly | QFile::Text)) {
903 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
904 } else {
905 f.open(QFile::ReadOnly | QFile::Text);
906 QTextStream ts(&f); 960 QTextStream ts(&f);
907 qApp->setStyleSheet(ts.readAll()); 961 qApp->setStyleSheet(ts.readAll());
908 GMainWindow::setStyleSheet(ts.readAll()); 962 GMainWindow::setStyleSheet(ts.readAll());
963 } else {
964 LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
909 } 965 }
910 theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme}); 966 theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme});
911 QIcon::setThemeName(":/icons/" + UISettings::values.theme); 967 QIcon::setThemeName(":/icons/" + UISettings::values.theme);
@@ -941,7 +997,6 @@ int main(int argc, char* argv[]) {
941 QCoreApplication::setOrganizationName("yuzu team"); 997 QCoreApplication::setOrganizationName("yuzu team");
942 QCoreApplication::setApplicationName("yuzu"); 998 QCoreApplication::setApplicationName("yuzu");
943 999
944 QApplication::setAttribute(Qt::AA_X11InitThreads);
945 QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 1000 QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
946 QApplication app(argc, argv); 1001 QApplication app(argc, argv);
947 1002
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 074bba3f9..6e335b8f8 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -9,6 +9,7 @@
9#include <QTimer> 9#include <QTimer>
10#include "core/core.h" 10#include "core/core.h"
11#include "ui_main.h" 11#include "ui_main.h"
12#include "yuzu/hotkeys.h"
12 13
13class Config; 14class Config;
14class EmuThread; 15class EmuThread;
@@ -43,7 +44,7 @@ public:
43 void filterBarSetChecked(bool state); 44 void filterBarSetChecked(bool state);
44 void UpdateUITheme(); 45 void UpdateUITheme();
45 GMainWindow(); 46 GMainWindow();
46 ~GMainWindow(); 47 ~GMainWindow() override;
47 48
48signals: 49signals:
49 50
@@ -172,6 +173,8 @@ private:
172 // stores default icon theme search paths for the platform 173 // stores default icon theme search paths for the platform
173 QStringList default_theme_paths; 174 QStringList default_theme_paths;
174 175
176 HotkeyRegistry hotkey_registry;
177
175protected: 178protected:
176 void dropEvent(QDropEvent* event) override; 179 void dropEvent(QDropEvent* event) override;
177 void dragEnterEvent(QDragEnterEvent* event) override; 180 void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index cea1a5e62..9bf26717f 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -105,6 +105,11 @@ void Config::ReadValues() {
105 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); 105 Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0);
106 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0); 106 Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 0.0);
107 107
108 // Audio
109 Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
110 Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
111 Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);
112
108 // Data Storage 113 // Data Storage
109 Settings::values.use_virtual_sd = 114 Settings::values.use_virtual_sd =
110 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); 115 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
@@ -114,6 +119,7 @@ void Config::ReadValues() {
114 119
115 // Miscellaneous 120 // Miscellaneous
116 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); 121 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
122 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
117 123
118 // Debugging 124 // Debugging
119 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); 125 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 567f23417..9a935a0d5 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -143,19 +143,17 @@ swap_screen =
143 143
144[Audio] 144[Audio]
145# Which audio output engine to use. 145# Which audio output engine to use.
146# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available) 146# auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available)
147output_engine = 147output_engine =
148 148
149# Whether or not to enable the audio-stretching post-processing effect.
150# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
151# at the cost of increasing audio latency.
152# 0: No, 1 (default): Yes
153enable_audio_stretching =
154
155# Which audio device to use. 149# Which audio device to use.
156# auto (default): Auto-select 150# auto (default): Auto-select
157output_device = 151output_device =
158 152
153# Output volume.
154# 1.0 (default): 100%, 0.0; mute
155volume =
156
159[Data Storage] 157[Data Storage]
160# Whether to create a virtual SD card. 158# Whether to create a virtual SD card.
161# 1 (default): Yes, 0: No 159# 1 (default): Yes, 0: No
@@ -166,6 +164,16 @@ use_virtual_sd =
166# 1: Yes, 0 (default): No 164# 1: Yes, 0 (default): No
167use_docked_mode = 165use_docked_mode =
168 166
167# Sets the account username, max length is 32 characters
168# yuzu (default)
169username =
170
171# Sets the systems language index
172# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
173# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
174# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese
175language_index =
176
169# The system region that yuzu will use during emulation 177# The system region that yuzu will use during emulation
170# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan 178# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
171region_value = 179region_value =
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index b5392c499..d637dbd0c 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -23,6 +23,7 @@
23#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 23#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
24 24
25#include <getopt.h> 25#include <getopt.h>
26#include "core/crypto/key_manager.h"
26#ifndef _MSC_VER 27#ifndef _MSC_VER
27#include <unistd.h> 28#include <unistd.h>
28#endif 29#endif
@@ -71,6 +72,7 @@ static void InitializeLogging() {
71/// Application entry point 72/// Application entry point
72int main(int argc, char** argv) { 73int main(int argc, char** argv) {
73 Config config; 74 Config config;
75
74 int option_index = 0; 76 int option_index = 0;
75 bool use_gdbstub = Settings::values.use_gdbstub; 77 bool use_gdbstub = Settings::values.use_gdbstub;
76 u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); 78 u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port);
@@ -162,7 +164,7 @@ int main(int argc, char** argv) {
162 164
163 SCOPE_EXIT({ system.Shutdown(); }); 165 SCOPE_EXIT({ system.Shutdown(); });
164 166
165 const Core::System::ResultStatus load_result{system.Load(emu_window.get(), filepath)}; 167 const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
166 168
167 switch (load_result) { 169 switch (load_result) {
168 case Core::System::ResultStatus::ErrorGetLoader: 170 case Core::System::ResultStatus::ErrorGetLoader:
@@ -171,11 +173,15 @@ int main(int argc, char** argv) {
171 case Core::System::ResultStatus::ErrorLoader: 173 case Core::System::ResultStatus::ErrorLoader:
172 LOG_CRITICAL(Frontend, "Failed to load ROM!"); 174 LOG_CRITICAL(Frontend, "Failed to load ROM!");
173 return -1; 175 return -1;
174 case Core::System::ResultStatus::ErrorLoader_ErrorEncrypted: 176 case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys:
175 LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before " 177 LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and the keys required "
176 "being used with yuzu. \n\n For more information on dumping and " 178 "could not be found. Please refer to the yuzu wiki for help");
177 "decrypting games, please refer to: " 179 return -1;
178 "https://yuzu-emu.org/wiki/dumping-game-cartridges/"); 180 case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting:
181 LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and there was a "
182 "general error while decrypting. This could mean that the keys are "
183 "incorrect, game is invalid or game uses an unsupported method of "
184 "crypto. Please double-check your keys");
179 return -1; 185 return -1;
180 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat: 186 case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
181 LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported."); 187 LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
@@ -187,7 +193,7 @@ int main(int argc, char** argv) {
187 LOG_CRITICAL(Frontend, "Failed to determine system mode!"); 193 LOG_CRITICAL(Frontend, "Failed to determine system mode!");
188 return -1; 194 return -1;
189 case Core::System::ResultStatus::ErrorVideoCore: 195 case Core::System::ResultStatus::ErrorVideoCore:
190 LOG_CRITICAL(Frontend, "VideoCore not initialized"); 196 LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
191 return -1; 197 return -1;
192 case Core::System::ResultStatus::Success: 198 case Core::System::ResultStatus::Success:
193 break; // Expected case 199 break; // Expected case